Найти выигрышный ход в крестики-нолики с булевыми массивами в VBA


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

Цель-выход выигрышный ход, или 0, если они равны.

Ниже приведен пример ввода:

3
7 5 4 1 9 2 8 3 6
5 1 3 7 6 4 2 9 8
5 1 2 8 6 4 7 3 9

answer:
7 6 0

Он настроен на таблицу с A1 количество игр и каждая игра в строку с пробелами в следующие клетки. Не слишком важно, так как я просто привожу его в, а затем нажмите конец результата (C1).

Это, как говорится, потому что я смотрю на используя этот стиль для другой тип головоломки я думаю, что, возможно overkilled это - вероятно, я мог бы полностью пропустить CheckWin и просто запустить все три победы сценарии каждый раз, но это было бы нелепо с 81 коробки вместо 9 коробок.

Булевские переменные всегда инициализируются как False, который объяснить Не-результате для некоторых функций.

Есть

  1. Основной Суб
  2. Сделать шаг суб
  3. Регистрация для Win (общие) функции
  4. Функции для проверки по горизонтали, вертикали или диагонали, выигрывает
  5. Суб, чтобы сбросить boolean массивы всем неверным

Я передаю мои массивы ByRef так что я могу вернуться в один Boolean результат моей функции. Плюс они должны в любом случае сохранится.

Я знаю, есть много магических чисел и нет констант.


Option Explicit

Public Sub FindTicTacToeWinningMove()
    Dim results As String
    Dim numberOfGames As Long
    Dim index As Long
    numberOfGames = Sheet1.Cells(1, 1)
    Dim gameNumber As Long
    Dim moveNumber As Long
    Dim xBoxes(1 To 9) As Boolean
    Dim oBoxes(1 To 9) As Boolean
    Dim rawMoves As Variant
    Dim moves(1 To 9) As String

    For gameNumber = 2 To numberOfGames + 1
        ClearArrays xBoxes, oBoxes
        rawMoves = Split(Sheet1.Cells(gameNumber, 1), " ")
        For index = LBound(rawMoves) To UBound(rawMoves)
            moves(index + 1) = rawMoves(index)
        Next
        For moveNumber = 1 To 9
            Select Case moveNumber Mod 2
            Case 1
                If MakeMove(xBoxes, moves(moveNumber), moveNumber) Then
                    results = results & " " & moveNumber
                    GoTo Win
                End If
            Case 0
                If MakeMove(oBoxes, moves(moveNumber), moveNumber) Then
                    results = results & " " & moveNumber
                    GoTo Win
                End If
            End Select
        Next
        results = results & " " & 0
Win:
    Next
    Sheet1.Cells(1, 3) = Trim$(results)
End Sub

Private Function MakeMove(ByRef moveArray() As Boolean, ByVal position As Long, ByVal moveNumber As Long) As Boolean
    moveArray(position) = True
    If moveNumber < 5 Then
        MakeMove = False
        Exit Function
    End If
    MakeMove = CheckWin(moveArray, position)
End Function

Private Function CheckWin(ByRef moveArray() As Boolean, ByVal position As Long) As Boolean
    Select Case position Mod 3
    Case 1
        If moveArray(position + 1) Then
            If CheckHorizontal(moveArray, position) Then GoTo Win
        End If
        If position = 7 Then
            If moveArray(position - 3) Then
                If CheckVertical(moveArray, position) Then GoTo Win
            End If

        ElseIf moveArray(position + 3) Then
            If CheckVertical(moveArray, position) Then GoTo Win
        End If

    Case 2
        If moveArray(position - 1) Then
            If CheckHorizontal(moveArray, position) Then GoTo Win
        End If
        If position = 2 Then
            If moveArray(position + 3) Then
                If CheckVertical(moveArray, position) Then GoTo Win
            End If

        ElseIf moveArray(position - 3) Then
            If CheckVertical(moveArray, position) Then GoTo Win
        End If

    Case 0
        If moveArray(position - 1) Then
            If CheckHorizontal(moveArray, position) Then GoTo Win
        End If
        If position = 9 Then
            If moveArray(position - 3) Then
                If CheckVertical(moveArray, position) Then GoTo Win
            End If
        ElseIf moveArray(position + 3) Then
            If CheckVertical(moveArray, position) Then GoTo Win
        End If
    End Select
    If position Mod 2 = 1 Then
        If CheckDiagonal(moveArray) Then GoTo Win
    End If
    Exit Function
Win:
    CheckWin = True
End Function

Private Function CheckHorizontal(ByRef moveArray() As Boolean, ByVal position As Long) As Boolean
    Select Case position
    Case 1, 2, 3
        If moveArray(1) And moveArray(2) And moveArray(3) Then CheckHorizontal = True
    Case 4, 5, 6
        If moveArray(4) And moveArray(5) And moveArray(6) Then CheckHorizontal = True
    Case 7, 8, 9
        If moveArray(7) And moveArray(8) And moveArray(9) Then CheckHorizontal = True
    End Select
End Function

Private Function CheckVertical(ByRef moveArray() As Boolean, ByVal position As Long) As Boolean
    Select Case position
    Case 1, 4, 7
        If moveArray(1) And moveArray(4) And moveArray(7) Then CheckVertical = True
    Case 2, 5, 8
        If moveArray(2) And moveArray(5) And moveArray(8) Then CheckVertical = True
    Case 3, 6, 9
        If moveArray(3) And moveArray(6) And moveArray(9) Then CheckVertical = True
    End Select
End Function

Private Function CheckDiagonal(ByRef moveArray() As Boolean) As Boolean
    If moveArray(5) And moveArray(1) And moveArray(9) Then CheckDiagonal = True
    If moveArray(5) And moveArray(3) And moveArray(7) Then CheckDiagonal = True
End Function

Private Sub ClearArrays(ByRef firstArray() As Boolean, ByRef secondArray() As Boolean)
    Dim index As Long
    For index = 1 To 9
        firstArray(index) = False
        secondArray(index) = False
    Next
End Sub


Комментарии
1 ответ

Отличная работа по этой проблеме кодирования.

ClearArrays: Подпрограмма

Эта функция не требуется. Использовать встроенные в VBA Erase метод вместо этого.


Стереть иксбоксы

Стереть oBoxes

FindTicTacToeWinningMove: Подпрограмма

В качестве личного предпочтения, я бы просто использовать 0 массивов.

rawMoves = Split(Sheet1.Cells(gameNumber, 1), " ")
For index = LBound(rawMoves) To UBound(rawMoves)
moves(index + 1) = rawMoves(index)
Next

В следующем блоке кода я бы:


  • Заменить Select Case С If Else заявление. Select Case заявления довольно длительный, чтобы использовать для всего в 2 случаях.

  • GoTo заявления следует избегать, если вы не пишете обработчик ошибок. Можно заменить GoTo Win С Exit For. Хитрость заключается в том, чтобы воспользоваться как For Next цикл работ. После Next счетчик увеличивается и условие выхода проверяется. Если значение счетчика превышает условие выхода, то цикл завершается. Если For moveNumber = 1 To 9 работает непрерывно, то moveNumber = 10 после завершения цикла еще moveNumber будет равна от 1 до 9 в зависимости от Exit For был казнен.


Для moveNumber = 1 до 9
Выберите случае moveNumber мод 2
Корпус 1
Если MakeMove(иксбоксы, движется(moveNumber), moveNumber) тогда
результаты = результаты & "" & moveNumber
Гото Выиграть
Конец Если
Случае 0
Если MakeMove(oBoxes, движется(moveNumber), moveNumber) тогда
результаты = результаты & "" & moveNumber
Гото Выиграть
Конец Если
Конец Выбора
Далее

Я более сжатой форме, чтобы написать приведенный выше код выглядит следующим образом:

    For moveNumber = 1 To 9
If moveNumber Mod 2 Then
If MakeMove(xBoxes, moves(moveNumber), moveNumber) Then Exit For
Else
If MakeMove(oBoxes, moves(moveNumber), moveNumber) Then Exit For
End If
Next
results = results & " " & IIf(moveNumber = 10, 0, moveNumber)

Конечно, мы могли бы конденсироваться ее дальше, но это выглядит как смешно:

    For moveNumber = 1 To 9
If MakeMove(IIf(CBool(moveNumber Mod 2), xBoxes, oBoxes), moves(moveNumber), moveNumber) Then Exit For
Next
results = results & " " & IIf(moveNumber = 10, 0, moveNumber)

MakeMove: Функция

Рекомендуется иметь булевых функций звучит как вопрос (например, isWin, hasWon, isGameOver). Следуя этому правилу, вы должны позвонить MakeMove и CheckWin отдельно. Это, как говорится, я видел много кода, что было Move возвращать логическое значение.

CheckWin, CheckHorizontal, CheckDiagonal

CheckWin является более сложным. Нет никаких причин, чтобы попробовать и оптимизировать работу кодов. Есть только 8 наборов 3 позиции в массиве, чтобы проверить.

Private Function hasWon(ByRef moveArray() As Boolean) As Boolean
Dim sequence As Variant
Dim a As Long, b As Long, c As Long
For Each sequence In Array(Array(1, 2, 3), Array(4, 5, 6), Array(7, 8, 9), _
Array(1, 4, 7), Array(2, 5, 8), Array(3, 6, 9), _
Array(5, 1, 9), Array(5, 3, 7))

hasWon = moveArray(sequence(0)) * moveArray(sequence(1)) * moveArray(sequence(2))
If hasWon Then Exit Function
Next
End Function

Примечание: результат умножения 3 позиции в Булевом массиве вместе будет 0, если какие-либо условия являются ложными и не ноль, если все условия верны. Это как Булева логика работает. Кроме того, я мог бы просто использовать And вместо *.

2
ответ дан 1 марта 2018 в 06:03 Источник Поделиться