Заливка для игры Go в Visual Basic

Я любитель Visual Basic. Я пытаюсь воссоздать игру Go, и я создал доску и умею размещать камни на пересечениях сетки.

Теперь я хочу начать снимать окруженные камни. Я поискал в Интернете и обнаружил, что заливка наводнения - лучший способ решить эту проблему. Однако я несколько дней искал в Интернете и не могу найти ничего, что можно было бы использовать или манипулировать, чтобы создать это. Я не понимаю никаких других языков программирования, поэтому я не могу использовать фрагменты кода Java и т. Д. И фрагменты информации для Visual Basic, которые я нашел, не имеют для меня особого смысла, поскольку я все еще новичок.

Я попытался начать это сам, начав с малого с ситуации «Если бы один камень был захвачен». У меня есть два представления для доски, одно объявлено как «сетка», а другое - как «place_stone».

«Сетка» - это настоящая доска, на которой пользователи нажимают, чтобы разместить свои камни. Place_stone - это копия этой доски, но я использовал «0», «1» и «2» для обозначения пустого, черного и белого соответственно. Я использую Windows Forms, чтобы воссоздать эту игру. Это фрагмент кода, который я написал для захвата камней:

        Private Sub Panel1_Click(sender As Object, e As EventArgs) Handles Panel1.Click
    Dim board As Panel = DirectCast(sender, Panel)

    ' Figure out where the user clicked: min = 0, max = (gridsize - 1)
    Dim pt As Point = board.PointToClient(Cursor.Position)
    Dim colWidth As Integer = (1 / (GridSize + 1)) * board.Size.Width
    Dim rowHeight As Integer = (1 / (GridSize + 1)) * board.Size.Height
    Dim gridPosition As New Point(Math.Min(Math.Max((pt.X / colWidth) - 1, 0), GridSize - 1), Math.Min(Math.Max((pt.Y / rowHeight) - 1, 0), GridSize - 1))
    Dim newcoordsx As Integer
    Dim newcoordsy As Integer

    ' Now do something with gridPosition:
    If Not Grid(gridPosition.X)(gridPosition.Y).HasValue Then 'If gird(x,y) is empty
        illegalmovelbl.Hide() ' Hides the "Illegal Move" Label
        If cp = True Then ' If current player is Black

Это та часть, где я застрял и понял, что кодирование для каждой ситуации займет слишком много времени. Мне удалось написать код для одной ситуации:

            newcoordsx = gridPosition.X + 1
            If placed_stone(newcoordsx, gridPosition.Y) = 2 Then
                newcoordsy = gridPosition.Y + 1
                If placed_stone(newcoordsx, newcoordsy) = 1 Then
                    newcoordsy = gridPosition.Y - 1
                    If placed_stone(newcoordsx, newcoordsy) = 1 Then
                        newcoordsx = gridPosition.X + 2
                        If placed_stone(newcoordsx, gridPosition.Y) = 1 Then
                            newcoordsx = gridPosition.X + 1
                            Grid(gridPosition.X)(gridPosition.Y) = True 'Place a black stone at Grid(x,y)
                            Grid(newcoordsx)(gridPosition.Y) = Nothing
                            placed_stone(newcoordsx, gridPosition.Y) = 0
                            pass = False
                            cp = False
                            passbtn.BackColor = Color.White 'The passbutton changes colour to white
                            passbtn.ForeColor = Color.Black 'The passbutton font changes colour to black
                        End If
                    End If
                End If
            End If

            'Grid(gridPosition.X)(gridPosition.Y) = True ' Place a black stone at Grid(x,y)
            'placed_stone(gridPosition.X, gridPosition.Y) = 1
            'pass = False
            'cp = False
            'passbtn.BackColor = Color.White ' The passbutton changes colour to white
            'passbtn.ForeColor = Color.Black ' The passbutton font changes colour to black

        ElseIf cp = False Then ' If current player is White
            Grid(gridPosition.X)(gridPosition.Y) = False ' Place a white stone at Grid(x,y)
            placed_stone(gridPosition.X, gridPosition.Y) = 2
            pass = False
            cp = True
            passbtn.BackColor = Color.Black ' The passbutton changes colour to black
            passbtn.ForeColor = Color.White ' The passbutton font changes colour to white
        End If

    ElseIf Grid(gridPosition.X)(gridPosition.Y).HasValue Then ' If gird(x,y) isn't empty
        illegalmovelbl.Show() ' Shows the "Illegal Move" Label
        MsgBox("Place your stone in a vacant point") ' Displays error message
    End If

    board.Invalidate() ' Force the board to redraw itself
End Sub

Я пробовал использовать алгоритм Википедии для заливки наводнением, и я понимаю логику его работы, но я просто не знаю, как его запрограммировать на Visual Basic.

 Flood-fill (node, target-color, replacement-color):
 1. If target-color is equal to replacement-color, return.
 2. If the color of node is not equal to target-color, return.
 3. Set the color of node to replacement-color.
 4. Perform Flood-fill (one step to the south of node, target-color, replacement-color).
    Perform Flood-fill (one step to the north of node, target-color, replacement-color).
    Perform Flood-fill (one step to the west of node, target-color, replacement-color).
    Perform Flood-fill (one step to the east of node, target-color, replacement-color).
 5. Return.

Конечно, в Go вместо того, чтобы раскрашивать область, вам нужно убирать камни при захвате, и вы не начинаете заливку с камня, который вы только что поместили для захвата, вы начинаете с ближайшего камня, который хотите захватить. .

Не могли бы вы объяснить, как легко использовать заливку в Visual Basic и как реализовать это в этой игре в го?

Если кто-то захочет просмотреть весь код, дайте мне знать. Буду признателен за любые предложения!


person user6297009    schedule 03.01.2018    source источник
comment
Вот неплохое руководство по созданию заливка множеством разных способов. К сожалению, он написан на C #, но я думаю, что концепция является более важной частью.   -  person Bradley Uffner    schedule 03.01.2018


Ответы (1)


Я не знаком с правилами / игровым процессом игры Go, поэтому я не совсем уверен, что вы пытаетесь достичь, но если вы считаете, что алгоритм типа заполнения наводнения - это то, что вам нужно, тогда я могу по крайней мере дать совет, как это сделать. В первую очередь ваш код должен быть разбит на более детальные методы. Какие действия вы пытаетесь выполнить при щелчке по панели? Конечно, это не что-то одно. Происходит много разных вещей, каждая из которых может быть выполнена отдельным специализированным методом. Например, если у вас был такой метод:

Private Function GetGridPosition(board As Panel, cursorPosition As Point) As Point
    Dim pt As Point = board.PointToClient(Cursor.Position)
    Dim colWidth As Integer = (1 / (GridSize + 1)) * board.Size.Width
    Dim rowHeight As Integer = (1 / (GridSize + 1)) * board.Size.Height
    Return New Point(Math.Min(Math.Max((pt.X / colWidth) - 1, 0), GridSize - 1), Math.Min(Math.Max((pt.Y / rowHeight) - 1, 0), GridSize - 1))        
End Function

Затем в обработчике событий Panel1_Click вы можете значительно упростить начало кода, например:

Private Sub Panel1_Click(sender As Object, e As EventArgs) Handles Panel1.Click
    Dim board As Panel = DirectCast(sender, Panel)
    Dim gridPosition As Point = GetGridPosition(board, Cursor.Position)
' ...

Конечно, это делает код более организованным и легким для чтения, но это не приближает вас к алгоритму заливки, не так ли? Что ж, да, в основном это правда, но организация и удобочитаемость сами по себе являются достойными целями, так что давайте все равно продолжим ... Следующий шаг, который нам нужно выполнить, - это сделать ход игрока, а затем, если этот ход был успешным, нам нужно переключиться на другого игрока. Итак, давайте сначала создадим метод для переключения игроков:

Private Sub SwitchPlayer()
    pass = False
    cp = Not cp
    passbtn.BackColor = GetPlayerForeColor(cp)
    passbtn.ForeColor = GetPlayerBackColor(cp)
End Sub

Private Function GetPlayerForeColor(player as Boolean) As Color
    If player Then
        Return Color.White
    Else
        Return Color.Black
    End If 
End Function

Private Function GetPlayerBackColor(player as Boolean) As Color
    If player Then
        Return Color.Black
    Else
        Return Color.White
    End If 
End Function

Вы заметите, что я подкрался (автоматическое заклинание Chrome говорит мне, что это не слово, но мое американское воспитание требует иного) еще парочку методов, пока я этим занимался. Я уверен, что их цель очевидна. Но остановись прямо здесь. Это очевидно? Вы заметите, что комментарии исчезли, но смысл кода все еще очевиден. Вот что мы подразумеваем под самодокументированным кодом. Комментарии хороши, когда они необходимы, но еще лучше, когда они вообще не нужны.

Итак, представьте, что у нас есть такой метод:

Private Function MakeMove(gridPosition As Grid, player As Boolean) As Boolean
    ' return true if the move was successful
End Function

Тогда весь обработчик событий Panel1_Click мог бы выглядеть так:

Private Sub Panel1_Click(sender As Object, e As EventArgs) Handles Panel1.Click
    Dim board As Panel = DirectCast(sender, Panel)
    Dim gridPosition As Point = GetGridPosition(board, Cursor.Position)
    If MakeMove(gridPosition, cp) Then
        SwitchPlayer()
    Else
        ShowIllegalMoveMessage()
    End If
End Sub

Private Sub ShowIllegalMoveMessage()
    illegalmovelbl.Show() 'Shows the "Illegal Move" Label
    MsgBox("Place your stone in a vacant point") 'Displays error message
End Sub

Хорошо, теперь мы переходим к сути. Итак, какие шаги необходимо предпринять, когда делается ход? Ну, не знаю, потому что не знаю игры. Я оставляю это упражнение на ваше усмотрение, но, если ваши наклонности правильные и вам нужен какой-то алгоритм заполнения наводнения, то это, вероятно, означает, что вам нужно какое-то действие, которое можно повторять снова и снова, так что это должно быть собственным методом:

Private Sub PlaceStone(gridPosition As Point, player As Boolean)
    ' Do something
End Sub

Очевидно, Do something - ключевая часть всего этого, и это единственная часть, с которой я не могу вам помочь. Но, если это будет алгоритм заливки, я могу дать вам очень большую подсказку. Помимо всего прочего, он будет снова вызывать PlaceStone, передавая ему другую позицию в сетке (одну из окружающих позиций). Так, например, что-то вроде этого:

Private Sub PlaceStone(gridPosition As Point, player As Boolean)
    Dim north As Position = GetNorthPosition(gridPosition)
    If Floodable(north, player) Then
        PlaceStone(north, player)
    End If
    ' ...
End Sub

Когда метод вызывает себя таким образом, мы называем это рекурсией. Но до тех пор, пока вы не начнете разбивать свой код на отдельные небольшие методы, каждый со своей инкапсулированной задачей, вы действительно не сможете добавить рекурсию. Итак, сначала организуйтесь, а затем добавьте рекурсию.

person Steven Doggart    schedule 03.01.2018
comment
Спасибо за помощь! Я не совсем понимаю, как реализовать Position = GetNorthPosition(gridPosition) как код. Я пытался сделать Dim north As Integer = gridPosition.X + 1. Имеет ли это тот же эффект, что и вы предложили? Кроме того, что такое floodable? Сначала я подумал, что это может быть функция, но тут возникает ошибка .... Извините, я только новичок - person user6297009; 03.01.2018
comment
Floodable было просто придуманным именем функции, которое я использовал в качестве примера. Идея заключалась в том, что это будет функция, которая будет принимать позицию в сетке и игрока и возвращать Boolean, указывающее, должно ли действие заливки распространяться на эту позицию в сетке. Вам нужно будет реализовать какой-то метод, который что-то делает в этом направлении. Либо так, либо вы всегда можете просто вызвать PlaceStone для каждого направления и просто выйти, если он обнаружит, что он недействителен. Поскольку я не знаком с правилами или игрой, я не знаю точно, как это должно работать или как это должно называться. - person Steven Doggart; 03.01.2018
comment
GetNorthPosition был, опять же, просто придуманной функцией, которую я использовал в качестве примера, чтобы показать общую идею. Поскольку я не знаком с тем, чего вы пытаетесь достичь, я не знаю, имеет ли вообще смысл иметь такой метод в вашем контексте. Однако, вообще говоря, с алгоритмом заливки, метод рекурсивной заливки вызывает себя один раз для каждой смежной позиции в каждом направлении (например, один раз на север, один раз на юг, восток, запад или вверх, вниз, влево, правильно, если хотите). Итак, идея заключалась в том, чтобы взять Point и вернуть следующий Point на север. - person Steven Doggart; 03.01.2018