对于井字棋游戏什么算法,我可以用它来确定“最佳移动”的AI?(What algorithm for

2019-06-18 07:19发布

在井字棋实现我猜测,挑战性的部分是确定由机器打出了最好的举措。

什么是可以追求的算法? 我在寻找到从简单到复杂的实现。 我怎么会去解决这个问题的一部分?

Answer 1:

维基百科玩一场完美的比赛(赢或领带每次)的战略似乎是简单的伪代码:

引用自维基百科(井字#策略)

玩家可以玩井字棋的一场完美的比赛(赢或者,至少,画出),如果他们选择从下面的列表,每个回合第一个可用的举动,如在纽维尔使用和西蒙的1972年井字棋程序。[6]

  1. WIN:如果连续有两个,打第三次拿到三连胜。

  2. 块:如果对手有两成一排,打第三阻止他们。

  3. 叉:创建一个机会,在这里你可以用两种方法来赢得。

  4. 阻止对手的叉:

    选项1:连续创建两个对手迫使防守,只要它不创造他们叉子或获奖结果。 例如,如果“X”有一个角落,“O”的圆心,而“X”具有相反的角落里为好,“O”为了赢得一定不能玩一个角落。 (打在这种情况下一个角落里创造了“X”,赢得了叉。)

    选项2:如果有一种配置,其中对手可以fork,阻止叉。

  5. 中心:打中锋。

  6. 对角:如果对手是在角落里,打对角。

  7. 空角:玩一个空的角落。

  8. 空方:玩的空方。

认识到什么是“叉”的情况看起来像一个强力的方式可以做到的建议。

注:一个“完美”的对手是一个很好的锻炼,但最终不值得“玩”反对。 你可以,但是,改变上述各优先给予特征的弱点对手的个性。



Answer 2:

你需要什么(对于井字棋或一个更为艰难的比赛就像国际象棋)是极大极小算法 ,或其稍微复杂的变体, α-β剪枝 。 普通天真极大极小将与小井字棋搜索领域的游戏规则做精,虽然。

概括地说,你想要做的不是搜索具有最适合你的可能结果的举动,而是为了在最坏可能的结果是尽可能好招。 如果你认为你的对手是打最佳,你必须承担他们将采取最糟糕的是你中招了,所以你必须要最大限度地减少他们的最大增益的举动。



Answer 3:

生成每一个可能的电路板,并根据其后来产生进一步下跌的树并不需要多大的内存,尤其是当你认识到90度的旋转板是多余的,因为是围绕垂直翻转板得分它的蛮力方法,水平,和对角轴。

一旦你到这一点,有一些像不到1K的数据在一个树形图来描述的结果,从而为电脑的最佳举措。

-亚当



Answer 4:

对于井字棋典型的算法中应该是这样的:

板:表示板上的九元向量。 我们存储2(表示空白),3(指示X)或5(指示O)。 打开:一个整数,指示将要玩的游戏的哪些举动。 第1招将1来表示,最后由9。

算法

主要的算法使用三个功能。

Make2:返回5如果电路板的中心正方形是空白即,如果板[5] = 2。 否则,此函数返回的任何非角方(2,4,6或8)。

Posswin(P):返回值为0,玩家P不能在他的下一步行动赢; 否则返回构成一个成功的举动平方数。 此功能将使程序都取胜,以阻止对手获胜。 该功能通过检查每一行,列和对角线的动作。 通过对一整行(或列或对角线)每平方的值相乘,一赢的可能性可以被检查。 如果产品18(3×3×2),则X可以获胜。 如果产品50(5×5×2),然后与O可以获胜。 如果找到了赢球的行(列或对角线),在它的空白方能够确定和正方形的数量由这个函数返回。

去(N):使方n中的举动。 此过程集合板[n]至3如果打开是奇数,或5如果打开是偶数。 它还通过一个递增转。

该算法对每个移动内置的策略。 它使奇数移动,如果它扮演X,偶数的举动,如果它起着O.

Turn = 1    Go(1)   (upper left corner).
Turn = 2    If Board[5] is blank, Go(5), else Go(1).
Turn = 3    If Board[9] is blank, Go(9), else Go(3).
Turn = 4    If Posswin(X) is not 0, then Go(Posswin(X)) i.e. [ block opponent’s win], else Go(Make2).
Turn = 5    if Posswin(X) is not 0 then Go(Posswin(X)) [i.e. win], else if Posswin(O) is not 0, then Go(Posswin(O)) [i.e. block win], else if Board[7] is blank, then Go(7), else Go(3). [to explore other possibility if there be any ].
Turn = 6    If Posswin(O) is not 0 then Go(Posswin(O)), else if Posswin(X) is not 0, then Go(Posswin(X)), else Go(Make2).
Turn = 7    If Posswin(X) is not 0 then Go(Posswin(X)), else if Posswin(X) is not 0, then Go(Posswin(O)) else go anywhere that is blank.
Turn = 8    if Posswin(O) is not 0 then Go(Posswin(O)), else if Posswin(X) is not 0, then Go(Posswin(X)), else go anywhere that is blank.
Turn = 9    Same as Turn=7.

我已经用它。 让我知道你们的感受。



Answer 5:

既然你只处理的可能位置的3x3矩阵,这将会是很容易只写通过所有可能的搜索,而无需占用你的计算能力。 对于每一个开放的空间,通过计算后标志着空间(递归,我会说),然后使用以质取胜的最可能的举动所有可能的结果。

优化这将是徒劳无功的,真的。 虽然有些难办可能是:

  • 首先检查可能的赢其他球队,阻止你找到的第一个(如果有2场比赛反正)。
  • 始终以中心,如果它是开放的(和以前的规则没有候选人)。
  • 采取提前角边的(同样,如果先前的规则是空的)


Answer 6:

您可以让AI发挥自身在一些样本游戏学习的榜样。 使用监督学习算法,以帮助它一起。



Answer 7:

不使用游戏区的尝试。

  1. 赢(你的双)
  2. 如果不是,不能失去(对手的双)
  3. 如果没有,你已经有了一个叉(有两双)
  4. 如果不是,如果对手有一个叉
    1. 在阻断点可能双叉(最终夺冠)搜索
    2. 如果没有搜索堵点叉(这给了对手最失败的可能性)
    3. 如果不仅堵点(不丢失)
  5. 如果没有搜索双叉(最终夺冠)
  6. 如果不是只搜索叉这就给对手的最失败的可能性
  7. 如果不仅搜索双
  8. 如果不是山穷水尽,领带,随意。
  9. 如果不是(这意味着你的第一个举动)
    1. 如果这是游戏的第一个举动;
      1. 给予对手最失败的可能性(在只有拐角处的算法结果这给7点失败的可能性,对手)
      2. 或打破无聊只是随机的。
    2. 如果这是比赛的第二招;
      1. 发现只有未失分(给多一点的选择)
      2. 或发现该名单中有最好的获胜机会点(也可以是枯燥的,因为它会导致只有各个角落或相邻的角或中心)

注意:当你有双和叉子,检查你的双给对手double.if它给,检查你的新的强制性点包括在你的叉子清单。



Answer 8:

排名每个数字分值的平方。 如果一个正方形取,移动到下一个选择(排序按排名降序排列)。 你将需要选择一个策略(有换去第一和三个(我认为)的第二两个主要的)。 从技术上讲,你可以只编写所有的策略,然后选择一个随机的。 这将使一个难以预料的对手。



Answer 9:

这个答案假定您了解实现的P1完美的算法,并讨论了如何实现对普通的人类的球员,谁就会犯一些错误更普遍比其他条件的胜利。

当然,游戏应该以平局结束时,如果两个球员发挥最佳。 在一个人的水平,P1在一个角落里玩更经常产生获胜。 无论出于何种原因,心理,P2为诱饵,以为在玩心不那么重要,这是不幸的他们,因为它不创造P1游戏胜出的唯一回应。

如果P2 正确的中心块,P1应该起到相反的角落里,因为再次,无论出于何种心理原因,P2会喜欢玩一个角落,这又产生一个失败的主板为他们的对称性。

对于任何举动P1可就起步之举,有一招P2可能会做出将创建P1的胜利,如果双方球员此后发挥最佳。 在这个意义上P1可以玩的地方。 边缘举动是在这个意义上,可能的反应,此举动的最大部分产生平局最弱的,但仍存在,这将创造P1一场胜利回应。

经验(更准确地说,闲谈)的最佳起点P1移动似乎是第一个弯道,第二中心,和最后一条边。

您可以添加,亲自或通过GUI的下一个挑战,是不显示板。 一个人绝对可以记住所有的状态,但更大的挑战导致对称板,它需要较少的努力,记住的偏好,导致我在第一个分支中列出的错误。

我有很多在聚会的乐趣,我知道了。



Answer 10:

下面是考虑所有可能的移动解决方案,以及各自的后果移动以确定最佳的举措。

我们需要一个代表线路板数据结构的研究。 我们将代表董事会二维数组。 外阵列表示整个板,和一个内阵列表示一个行。 下面是一个空板的状态。

_gameBoard: [
    [“”, “”, “”],
    [“”, “”, “”],
    [“”, “”, “”]
]

我们将填充板用“X”和“O”字符。

下一步,我们将需要一个可检查的结果的功能。 该功能将检查字符的继承。 什么都板的状态,其结果是4个选项:要么不完整,播放器X韩元,球员Ø赢或平局。 函数应该返回这是董事会的状态。

 const SYMBOLS = { x:'X', o:'O' } const RESULT = { incomplete: 0, playerXWon: SYMBOLS.x, playerOWon: SYMBOLS.o, tie: 3 } function getResult(board){ // returns an object with the result let result = RESULT.incomplete if (moveCount(board)<5){ {result} } function succession (line){ return (line === symbol.repeat(3)) } let line //first we check row, then column, then diagonal for (var i = 0 ; i<3 ; i++){ line = board[i].join('') if(succession(line)){ result = symbol; return {result}; } } for (var j=0 ; j<3; j++){ let column = [board[0][j],board[1][j],board[2][j]] line = column.join('') if(succession(line)){ result = symbol return {result}; } } let diag1 = [board[0][0],board[1][1],board[2][2]] line = diag1.join('') if(succession(line)){ result = symbol return {result}; } let diag2 = [board[0][2],board[1][1],board[2][0]] line = diag2.join('') if(succession(line)){ result = symbol return {result}; } //Check for tie if (moveCount(board)==9){ result=RESULT.tie return {result} } return {result} } 

现在我们可以添加getBestMove功能,我们提供任何给定板,和下一个符号,该函数将运行检查与功能的getResult所有可能的行动。 如果它是一个双赢它会给它一个得分1.如果它是一个松散的,将得到-1分,平局一定的分值为0,如果它是不确定的,我们将在getBestMove功能递归找出得分的下一步行动。 由于下一个动作是oponent的,他的胜利是失去当前玩家,比分将被否定。 在最后可能的移动接收得分要么1,0或-1,我们可以把移动进行排序,并返回得分最高的举动。

  function getBestMove (board, symbol){ function copyBoard(board) { let copy = [] for (let row = 0 ; row<3 ; row++){ copy.push([]) for (let column = 0 ; column<3 ; column++){ copy[row][column] = board[row][column] } } return copy } function getAvailableMoves (board) { let availableMoves = [] for (let row = 0 ; row<3 ; row++){ for (let column = 0 ; column<3 ; column++){ if (board[row][column]===""){ availableMoves.push({row, column}) } } } return availableMoves } let availableMoves = getAvailableMoves(board) let availableMovesAndScores = [] for (var i=0 ; i<availableMoves.length ; i++){ let move = availableMoves[i] let newBoard = copyBoard(board) newBoard = applyMove(newBoard,move, symbol) result = getResult(newBoard,symbol).result let score if (result == RESULT.tie) {score = 0} else if (result == symbol) { score = 1 } else { let otherSymbol = (symbol==SYMBOLS.x)? SYMBOLS.o : SYMBOLS.x nextMove = getBestMove(newBoard, otherSymbol) score = - (nextMove.score) } if(score === 1) return {move, score} availableMovesAndScores.push({move, score}) } availableMovesAndScores.sort((moveA, moveB )=>{ return moveB.score - moveA.score }) return availableMovesAndScores[0] } 

算法在行动 , Github上 , 在讲解过程中更多的细节



文章来源: What algorithm for a tic-tac-toe game can I use to determine the “best move” for the AI?