Алгоритм Minmax выигрывает только тогда, когда ход находится в поле зрения В противном случае всегда позволяя игроку выиграть - PullRequest
0 голосов
/ 07 февраля 2012

Я три дня дергал себя за волосы, пытаясь понять, что пошло не так в том небольшом коде, который у меня есть в моей первой попытке как минимаксного алгоритма, так и рекурсивных вызовов в целом (я относительно новичок в программировании).По сути, у меня есть все, что работает в приложении, за исключением того, что я хотел на самом деле выучить и поработать в нем: алгоритм минимакса.

Обычно, когда игрок делает ход, компьютер выполняет одно из двух действий:

  • Если рядом с ним есть выигрышный ход, он будет использовать этот ход.Легко как пирог.
  • Однако, если этот ход не в поле зрения, он выберет любой ход , позволяющий игроку выиграть. Полная противоположность тому, что он должен делать.

Я знаю, что это не от:

  • Получатель легального движения
  • Сам оценщик доски, не знаю, может ли это быть из-за какой-то странной вещис указателем на него, но оценщик возвращает правильные оценки.

Вот код (я вырезал некоторые функции, которые запускают программу):

Public Structure Board
    Public lbl As Label
    Public owner As String
    Public posX As Integer
    Public posY As Integer
End Structure

Public Structure LegalMove
    Public posX As Integer
    Public posY As Integer
End Structure

Public Structure Best
    Public Move As LegalMove
    Public Score As Integer
End Structure

Public Class Form1
    Public Const PLAYER_PIECE As String = "X"
    Public Const COMPUTER_PIECE As String = "O"
    Public Const HUMAN_WIN As Integer = -1
    Public Const COMPUTER_WIN As Integer = 1
    Public Const TIE As Integer = 0
    Public Const COMPUTER As Boolean = True
    Public Const HUMAN As Boolean = False
    Public Game_Ended As Boolean = False
    Public Turn As String = "Human"
    Public Board(2, 2) As Board
    'Sets all objects up (mostly labels, and the board)
Private Sub On_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Dim intindex As Integer
    Dim intindex2 As Integer
    For intindex = 0 To 2
        For intindex2 = 0 To 2
            Dim Label As New Label
            Label.Name = "lbl" & intindex & intindex2
            Label.AutoSize = False
            Label.TextAlign = ContentAlignment.MiddleCenter
            Label.Font = New Font("Arial", 48, FontStyle.Bold)
            Label.Size = New System.Drawing.Size(100, 100)
            Label.Location = New System.Drawing.Point(intindex * 100, intindex2 * 100)
            Label.BorderStyle = Windows.Forms.BorderStyle.FixedSingle
            Board(intindex, intindex2).lbl = Label
            Board(intindex, intindex2).posX = intindex
            Board(intindex, intindex2).posY = intindex2
            Me.Controls.Add(Label)
            AddHandler Board(intindex, intindex2).lbl.Click, AddressOf Player_Move
        Next
    Next
End Sub
'If a player clicks on one of the labels, it will attmpt to put a player piece on that tile, and direct the game to the computer's turn.
Sub Player_Move(ByVal sender As System.Object, ByVal e As System.EventArgs)
    Dim Current_Board As Board = GetBoard(sender)
    Dim Best As Best
    If Current_Board.owner = Nothing Then
        Board(Current_Board.posX, Current_Board.posY).owner = PLAYER_PIECE
        Board(Current_Board.posX, Current_Board.posY).lbl.Text = PLAYER_PIECE
        Call Check_Board(False, Nothing)
        If Game_Ended = False Then
            Turn = "Computer"
            Best = Get_Computer_Move(COMPUTER)
            Board(Best.Move.posX, Best.Move.posY).owner = COMPUTER_PIECE
            Board(Best.Move.posX, Best.Move.posY).lbl.Text = COMPUTER_PIECE
            Call Check_Board(False, Nothing)
        End If
        Game_Ended = False
        Turn = "Human"
    End If
End Sub

'Checks win/tie conditions. If it is a simulation (for ai), then it will return a number. If it is for legitimate checking, it will call the win function, or tie.
Function Check_Board(ByVal simulation As Boolean, ByVal side As Boolean)
    Dim intindex As Integer
    Dim intindex2 As Integer
    'Vertical Check
    For intindex = 0 To 2
        If Board(intindex, 0).owner = Board(intindex, 1).owner And Board(intindex, 1).owner = Board(intindex, 2).owner And Board(intindex, 0).owner <> Nothing Then
            If simulation = False Then
                Win()
            Else
                If Board(intindex, 0).owner = COMPUTER_PIECE Then
                    Return 1
                Else
                    Return -1
                End If
            End If
        End If
    Next
    'Horizantal Check
    For intindex = 0 To 2
        If Board(0, intindex).owner = Board(1, intindex).owner And Board(1, intindex).owner = Board(2, intindex).owner And Board(0, intindex).owner <> Nothing Then
            If simulation = False Then
                Win()
            Else
                If Board(0, intindex).owner = COMPUTER_PIECE Then
                    Return 1
                Else
                    Return -1
                End If
            End If
        End If
    Next
    'Diagonal Check
    Dim intoppindex As Integer
    Dim intoppindex2 As Integer
    For intindex = 0 To 2 Step 2
        For intindex2 = 0 To 2 Step 2
            If intindex = 0 Then
                intoppindex = 2
            Else
                intoppindex = 0
            End If
            If intindex2 = 0 Then
                intoppindex2 = 2
            Else
                intoppindex2 = 0
            End If
            If Board(intindex, intindex2).owner = Board(1, 1).owner And Board(1, 1).owner = Board(intoppindex, intoppindex2).owner And Board(intindex, intindex2).owner <> Nothing Then
                If simulation = False Then
                    Win()
                Else
                    If Board(1, 1).owner = COMPUTER_PIECE Then
                        Return 1
                    Else
                        Return -1
                    End If
                End If
            End If
        Next
    Next
    'Full Board
    Dim movedcount As Integer
    For intindex = 0 To 2
        For intindex2 = 0 To 2
            If Board(intindex, intindex2).owner <> Nothing Then
                movedcount += 1
            End If
        Next
    Next
    If movedcount = 9 Then
        If simulation = False Then
            MessageBox.Show("It is a tie. Resetting the board.")
            For intindex = 0 To 2
                For intindex2 = 0 To 2
                    Board(intindex, intindex2).owner = Nothing
                    Board(intindex, intindex2).lbl.Text = Nothing
                Next
            Next
            Game_Ended = True
        Else
            Return 0
        End If
    End If
    Return Nothing
End Function

'Allows labels to be processed in to the board
Public Function GetBoard(ByVal sender As Label)
    Dim intindex As Integer
    Dim intindex2 As Integer
    For intindex = 0 To 2
        For intindex2 = 0 To 2
            If Board(intindex, intindex2).lbl.Name = sender.Name Then
                Return Board(intindex, intindex2)
            End If
        Next
    Next
    Return Nothing
End Function

'If a player wins, it will display a message box and reset the board
Sub Win()
    MessageBox.Show(Turn & " has won. Resetting the board.")
    Dim intindex As Integer
    Dim intindex2 As Integer
    For intindex = 0 To 2
        For intindex2 = 0 To 2
            Board(intindex, intindex2).owner = Nothing
            Board(intindex, intindex2).lbl.Text = Nothing
        Next
    Next
    Game_Ended = True
End Sub

'Minmax algorithm that tries to get best possible move by accessing every possible scenario in the game tree. NOT WORKING. Returns a "best" object, that is then used to place the computer's piece.
Public Function Get_Computer_Move(ByVal side As Boolean)
    Dim mybest As New Best
    Dim reply As New Best
    Dim LegalMoveslst As List(Of LegalMove)
    LegalMoveslst = Get_Legal_Moves(Board)

    'This allows to look at other's next move.
    Dim oppside As Boolean
    If side = COMPUTER Then
        oppside = HUMAN
    Else
        oppside = COMPUTER
    End If

    'At lowest end of a given branch (win, loss, or tie), the current score is returned.
    mybest.Score = Check_Board(True, side)
    If mybest.Score <> Nothing Then
        Return mybest
    End If

    'Base values so something is always there.
    If side = COMPUTER Then
        mybest.Score = -2
    Else
        mybest.Score = 2
    End If

    For Each LegalMove In LegalMoveslst
        If side = COMPUTER Then
            Board(LegalMove.posX, LegalMove.posY).owner = COMPUTER_PIECE
        Else
            Board(LegalMove.posX, LegalMove.posY).owner = PLAYER_PIECE
        End If
        reply = Get_Computer_Move(oppside)
        Board(LegalMove.posX, LegalMove.posY).owner = Nothing
        If ((side = COMPUTER And reply.Score > mybest.Score) Or (side = HUMAN And reply.Score < mybest.Score)) Then
            mybest.Move = LegalMove
            mybest.Score = reply.Score
        End If
    Next
    Return mybest
End Function

'Returns potential legal moves on the board
Public Function Get_Legal_Moves(ByVal tempBoard(,) As Board)
    Dim intindex As Integer
    Dim intindex2 As Integer
    Dim legalmoves As New List(Of LegalMove)
    For intindex = 0 To 2
        For intindex2 = 0 To 2
            If tempBoard(intindex, intindex2).owner = Nothing Then
                Dim legalmove As New LegalMove
                legalmove.posX = intindex
                legalmove.posY = intindex2
                legalmoves.Add(legalmove)
            End If
        Next
    Next
    Return legalmoves
End Function
End Class

Надеюсь, вы можете помочь!

Ответы [ 2 ]

0 голосов
/ 07 февраля 2012

Я не вижу очевидной проблемы с вашим кодом (но тогда, если бы он был, он не работал бы на SO), но я подозреваю, что вы инициализируете свои "базовые" значения в обратном порядке.Например, при расчете для COMPUTER вы принимаете значение reply.Score, если оно выше mybest.Score, но начальное значение, которое должно быть достаточно низким, равно 2. И это противоположно начальному значению другой стороны -2.Я не читал ваш код оценки, поэтому я не знаю, может ли это быть проблемой или нет.Может быть, в этом случае начальное значение 2 просто никогда не превзойдет?

0 голосов
/ 07 февраля 2012

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

На уровне AI не должно быть такой функции, как GetComputerBestMove. Только на уровне пользовательского интерфейса прога должна знать, кто будет думать на ходу. Что касается уровня искусственного интеллекта, у вас должна быть функция FindMyBestMove (side как Boolean, AllowedDepth как Integer) Это не касается имени. Эта функция не должна использовать значение «сторона» для чего-либо еще, кроме передачи ее противоположности той же функции при анализе движения противника. В некоторых играх стратегии для первого и второго игрока могут отличаться, там вы можете использовать их для оценки хода. Должно быть AllowedDepth - с каждым шагом рекурсии вниз и когда он достигает 0, функция может ' на этом шаге не использовать рекурсию. Вы можете использовать эту переменную для установки уровня ИИ.

...