【问题标题】:Minmax algorithm only winning when the move is in direct sight. Otherwise always allowing the player to winMinmax 算法仅在移动直接可见时获胜。否则总是让玩家获胜
【发布时间】:2012-02-07 08:51:07
【问题描述】:

三天来,我一直在努力找出我在第一次尝试极小极大算法和一般递归调用时所拥有的小代码中出了什么问题(我对编程比较陌生)。基本上,除了我想在其中实际学习和研究的东西:极小极大算法之外,我的应用程序中的所有东西都在工作。

基本上,每当玩家移动时,计算机都会执行以下两种操作之一:

  • 如果旁边有获胜的棋子,它将使用该棋子。很简单。
  • 但是,如果这个动作不是直接可见的,它将选择任何让玩家获胜的动作与它应该做的完全相反。

我知道它不是来自:

  • legalmove getter
  • 板评估器本身,不知道它是否来自带有指向它的指针的一些奇怪的东西,但评估器返回正确的分数。

这里是代码(我删掉了一些启动程序的函数):

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

希望你能帮忙!

【问题讨论】:

  • 15 年前,我在 VB 上为 Reversi 制作了一个比我更强大的程序。根据剩余时间深度思考。有效。是的,VB 不是一个方便的工具,但是当提问者转向Objective C 时,它会给他带来很多快乐。当然,我不是在谈论 Word VB 或 Excel VB。这是上帝的惩罚。
  • 您应该只在此处放置导致错误的部分。并自行本地化。在调试器中一步一步完成所有的tick-tack-toe 游戏并不难。在那里你会看到,最近的错误在哪里
  • 那是 VB.NET 代码而不是 VB6,在其中编码没有错。编译方式与 C# 相同。
  • 为了它的价值,作为一个练习,我将代码翻译成另一种语言,保持程序逻辑不变。它玩井字游戏没有问题。所以问题很可能是 VB 的一些微妙之处让你感到困惑,而不是你误解了 minimax 的工作原理。祝你好运。

标签: vb.net algorithm recursion minimax tic-tac-toe


【解决方案1】:

我在你的申请中迷路了。你违反了干净编程的规则。在这样的算法中,通过分支递归,这种恶习会受到惩罚。显然,您对现在正在分析谁的动作的问题感到困惑。

在AI层面不应该有GetComputerBestMove这样的功能。只有在 UI 级别 prog 需要知道,谁会在移动中思考。至于 AI 级别,你应该有函数 FindMyBestMove(side as Boolean, AllowedDepth as Integer) 不是关于名称的。这个函数不应该将“side”的值用于其他任何事情,除了在分析敌人的移动时将它的对立面传递给相同的函数。 在某些游戏中,第一个和第二个玩家的策略可能不同,你可以使用它来评估移动。 AllowedDepth 应该是 -- 随着每个递归步骤下降并且当它达到 0 时,该函数可以'不要在该步骤上使用递归。您可以使用此变量来设置 AI 的级别。

【讨论】:

  • 我主要是在风格方面自学(非常基本的高中计算机科学课程让我开始编程,并教我 vb.net 的语法,除了“cmets 很好”之外没有太多其他想法”)。我在哪里可以阅读干净编程的规则?是否只涉及将我的功能名称更改为 Get_Best_Move?关于我的实际问题,谢谢您的帮助。我肯定会使用调试器并查看哪一方在做什么,因为我对这个问题的怀疑也有一段时间了。另外,我不想为这个游戏设置alloweddepth。
  • 最后一件事,我将如何消除我的旁注?例如,我觉得有必要在 .owner 分配之类的东西中使其成为 COMPUTER_PIECE。有什么我不知道的策略吗?
  • 尝试在某处下载,或购买“实用程序员,从熟练的程序员到大师”,作者 Hunt, Thomas。它的风格非常好,不追求酷消息。
  • 您可以将其作为问题发布,采用更精致的风格。抱歉,我没有理解 .owner 上的问题。但请注意:样式问题不是针对此服务器,而是针对programmers.stackexchange.com,也许这对您也有帮助programmers.stackexchange.com/a/130855/44104
  • 顺便说一句,我也是自学成才的风格。只有我在 1985 年才开始使用它,并且是一本非常好的书。
【解决方案2】:

我没有看到你的代码有明显的问题(但是,如果有的话,它不会出现在 SO 上),但我怀疑你正在反向初始化你的“基础”值。例如,在计算 COMPUTER 时,如果高于 mybest.Score,则接受 reply.Score 的值,但初始值应该相当低,为 2。这与另一方的起始值相反 - 2.我没有阅读您的估算器代码,所以我不知道这是否有问题。也许在这种情况下,2 的初始值根本就不会被击败?

【讨论】:

  • 不,计算机端的分数设置为-2,因此任何实际返回都会覆盖它。不是这样的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-28
  • 1970-01-01
  • 2012-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多