简体   繁体   English

在TIC TAC TOE游戏中生成下一步动作的最快方法

[英]Fastest way to generate next move in TIC TAC TOE game

In a X's and 0's game (ie TIC TAC TOE(3X3) ) if you write a program for this give a fast way to generate the moves by the computer. 在X和0的游戏中(例如TIC TAC TOE(3X3) ),如果您为此编写了程序,则可以通过一种快速的方法来生成计算机的移动。 I mean this should be the fastest way possible. 我的意思是这应该是最快的方法。

All I could think of at that time is to store all the board configurations in a hash so that getting best position of move is a O(1) operation. 我当时唯一想到的就是将所有电路板配置存储在散列中,以便获得最佳移动位置是O(1)操作。
Each board square can be either 0,1, or 2. 每个板的平方可以是0,1或2。
0 represents empty square. 0表示空正方形。 1 represents a X & 2 represents 0. 1代表X,2代表0。

So every square can be filled with either of the three. 因此,每个方格均可填充三个方格中的任意一个。 There are approx 3^9 board configurations. 大约有3 ^ 9个电路板配置。

In simple, we need a hash of size 3^9. 简单来说,我们需要一个大小为3 ^ 9的哈希。 For hashing,we can go for base 3 representation. 对于散列,我们可以使用基数3表示。 Means each number in base 3 will be 9 digits long each digit corresponding to each square. 表示以3为底的每个数字将长9个数字,每个数字对应于每个正方形。 To search in hash, we need to find the decimal representation of this 9 digit number. 要搜索哈希,我们需要找到该9位数字的十进制表示形式。 Now, each square can be associated with row number & column number. 现在,每个正方形都可以与行号和列号关联。 In order to identify each square uniquely, we can again make use of base 3 representation. 为了唯一地标识每个正方形,我们可以再次使用以3为底的表示形式。 say SQ[1][2] will be 12 in base 3 which is equivalent to 5 in decimal. 假设SQ [1] [2]在基数3中将为12,相当于十进制的5。

Thus, we have effectively designed an algorithm which is fast enough to calculate the next move in O(1). 因此,我们有效地设计了一种算法,该算法的速度足以计算O(1)中的下一步。

But, the interviewer insisted in reducing the space complexity as DOS system doesn't have that much amount of memory. 但是,由于DOS系统没有那么多的内存,访问者坚持要减少空间的复杂性。

How can we reduce the space complexity with no change in time complexity? 如何在不改变时间复杂度的情况下降低空间复杂度?
Please help me so that I do not miss such type of questions in the future. 请帮助我,这样以后我就不会再错过此类问题了。

For a small game like this, a different way of going about this is to pre-compute and store the potential game tree in a table. 对于像这样的小型游戏,进行此操作的另一种方法是预先计算并将潜在的游戏树存储在表中。

Looking first at the situation where the human starts, she obvious has 9 different start positions. 首先看人类开始的情况,她显然有9个不同的开始位置。 A game-play table would contain 9 entry points, then, each pointing to the correct response - you could use the guidelines outlined in this question to calculate the responses - as well as the next level table of human responses. 游戏表将包含9个入口点,然后每个入口点都指向正确的响应-您可以使用此问题中概述的准则来计算响应-以及下一级人类响应。 This time there are only 7 possible responses. 这次只有7种可能的回应。 For the next level there'll be 5, then 3, then just 1. In total, there will be 9 * 7 * 5 * 3 * 1 = 945 entries in the table, but that can be compressed by realizing symmetries, ie rotations and flipped colors. 对于下一个级别,将有5个,然后是3个,然后只有1个。总共,表中将有9 * 7 * 5 * 3 * 1 = 945个条目,但是可以通过实现对称性(即旋转)来压缩和翻转的颜色。

Of course, the situation where the computer starts is similar in principle but the table is actually smaller because the computer will probably want to start by playing the middle piece - or at least avoid certain spots. 当然,计算机启动的情况在原理上是相似的,但是桌子实际上较小,因为计算机可能希望通过演奏中间乐曲来启动-或至少避免某些位置。

There are not 3^9 different board configurations. 没有3 ^ 9种不同的电路板配置。 Just as tomdemuyt says, there are 9! 就像tomdemuyt所说的那样,有9个! different board configurations, ie, 9 choices at first, 8 choices next, 7 choices after that, and so on. 不同的电路板配置,例如,首先9个选择,接下来8个选择,之后7个选择,依此类推。

Also, we can further reduce the space complexity by accounting for symmetry. 同样,我们可以通过考虑对称性来进一步降低空间复杂度。 For example, for the first move, placing an X in [0,0] is the same as placing it in [0,2], [2,0], and [2,2]. 例如,对于第一步,将X放置在[0,0]中与将X放置在[0,2],[2,0]和[2,2]中相同。 I believe this reduces 9! 我相信这会减少9! to 9!/4 至9!/ 4

We can even reduce that by accounting for which board configurations were winning before the final move (the 9th move). 我们甚至可以通过考虑在最后一步(第9步)之前获胜的主板配置来减少这一点。 I don't know the number, but a detailed explanation can be found on the Stack Overflow cousin http://en.wikipedia.org/wiki/Tic-tac-toe 我不知道这个数字,但是可以在Stack Overflow表兄弟中找到详细的说明,网址为http://en.wikipedia.org/wiki/Tic-tac-toe

The assumption of 3^9 is wrong. 3 ^ 9的假设是错误的。 This would include for example a board that only has X which is impossible as both players place each turn an X or an O. 例如,这将包括仅具有X的棋盘,这是不可能的,因为两个玩家每轮都将X或O放置。

My initial thought was that there are (9*8*7*6*5*4*3*2) * 2 possibilities. 我最初的想法是有(9 * 8 * 7 * 6 * 5 * 4 * 3 * 2)* 2种可能性。 First player has 9 choices, second player has 8 choices, first player has 7 etc. I put * 2 because you might have different best moves depending who starts. 第一个玩家有9个选择,第二个玩家有8个选择,第一个玩家有7个,依此类推。我放* 2,因为根据谁的先发,您可能会有不同的最佳移动。

Now 3^9 is 19863 and 9! 现在3 ^ 9是19863和9! is 362880, so clearly this is not the superior solution, a lot of 'different scenarios' actually will end up looking exactly the same. 是362880,因此显然这不是高级解决方案,实际上许多“不同方案”最终看起来都完全一样。 Still, the base idea that many of the 19863 board setups are invalid remain. 尽管如此,仍然存在许多19863电路板设置无效的基本思想。

This piece of code which probably could be replaced by a simple formula tells me that this is the count of positions you want to have a move for. 这段代码可能可以用一个简单的公式替换,这告诉我这是您想要移动的头寸数量。

<script>

a = permuteString( "X........" ); document.write( Object.keys(a).length + "<br>" );console.log( a );
a = permuteString( "XO......." ); document.write( Object.keys(a).length + "<br>" );console.log( a );
a = permuteString( "XOX......" ); document.write( Object.keys(a).length + "<br>" );console.log( a );
a = permuteString( "XOXO....." ); document.write( Object.keys(a).length + "<br>" );console.log( a );
a = permuteString( "XOXOX...." ); document.write( Object.keys(a).length + "<br>" );console.log( a );
a = permuteString( "XOXOXO..." ); document.write( Object.keys(a).length + "<br>" );console.log( a );
a = permuteString( "XOXOXOX.." ); document.write( Object.keys(a).length + "<br>" );console.log( a );

//Subset of the Array.prototype.slice() functionality for a string
function spliceString( s , i )
{
    var a = s.split("");
  a.splice( i , 1 );
    return a.join("");
}

//Permute the possibilities, throw away equivalencies
function permuteString( s )
{
    //Holds result
    var result = {};

    //Sanity
    if( s.length < 2 ) return [];

    //The atomic case, if AB is given return { AB : true , BA : true }
    if( s.length == 2 )
    {
        result[s] = true;
        result[s.charAt(1)+s.charAt(0)] = true;
        return result;
    }

    //Enumerate
    for( var head = 0 ; head < s.length ; head++ )
    {
        var o = permuteString( spliceString( s , head  ) );
        for ( key in o )
            result[ s.charAt( head ) + key ] = true;
    }
    return result;
}


</script>

This gives the following numbers: 1st move : 9 2nd move : 72 3rd move : 252 4th move : 756 5th move : 1260 6th move : 1680 7th move : 1260 这给出了以下数字:第一步:9第二步:72第三步:252第四步:756第五步:1260第六步:1680第七步:1260

So in total 5289 moves, this is without even checking for already finished games or symmetry. 因此,在总共5289次移动中,这甚至不检查已经完成的游戏或对称性。

These numbers allow you to lookup a move through an array, you can generate this array yourself by looping over all possible games. 这些数字使您可以查找数组中的移动,您可以通过遍历所有可能的游戏来自己生成此数组。

T. T.

The game of Tic Tac Toe is sufficiently simple that optimal algorithm may be implemented by a machine built from Tinker Toys (a brand of sticks and fasteners). Tic Tac Toe的游戏非常简单,可以用Tinker Toys(一种棍棒和紧固件品牌)制造的机器来实现最佳算法。 Since the level of hardware complexity encapsulated by such a construction is below that of a typical 1970's microprocessor, the time required to find out what moves have been made would in most cases exceed the time required to figure out the next move. 由于这种结构封装的硬件复杂性水平低于典型的1970年代微处理器的水平,因此在大多数情况下,找出进行了什么动作所需的时间将超过找出下一个动作所需的时间。 Probably the simplest approach would be have a table which, given the presence or absence of markers of a given player (2^9, or 512 entries), would indicate what squares would turn two-in-a-rows into three-in-a-rows. 可能最简单的方法是拥有一张表格,考虑到给定玩家的标记存在或不存在(2 ^ 9或512个条目),该表格将指示哪些正方形会将两行制变成三合一一排上。 Start by doing a lookup with the pieces owned by the player on move; 首先对移动中的玩家拥有的零件进行查找; if any square which would complete a three-in-a-row is not taken by the opponent, take it. 如果对手没有采取任何能够完成三合一的方格,那就采取它。 Otherwise look up the opponent's combination of pieces; 否则,请查看对手的棋子组合; any square it turns up that isn't already occupied must be taken. 出现的任何尚未占据的正方形都必须采取。 Otherwise, if the center is available, take it; 否则,如果有空的话,就去接; if only the center is taken, take a corner. 如果仅以中心为准,则拐角。 Otherwise take an edge. 否则要占优势。

It might be more interesting to open up your question to 4x4x4 Tic Tac Toe, since that represents a sufficient level of complexity that 1970's-era computer implementations would often take many seconds per move. 将您的问题公开为4x4x4 Tic Tac Toe可能会更有趣,因为这代表了足够的复杂性,以至于1970年代的计算机实现每次移动通常要花费几秒钟的时间。 While today's computers are thousands of times faster than eg the Atari 2600, the level of computation at least gets beyond trivial. 尽管当今的计算机比Atari 2600快数千倍,但计算水平至少不算微不足道。

If one extends the game to 4x4x4, there will be many possibilities for trading off speed, RAM, and code space. 如果将游戏扩展到4x4x4,将有很多权衡速度,RAM和代码空间的可能性。 Unlike the original game which has 8 winning lines, the 4x4x4 version has (IIRC) 76. If one keeps track of each line as being in one of 8 states [ten if one counts wins], and for each vacant square one keeps track of how many of the winning lines that pass through it are in what states, it should be possible to formulate some pretty fast heuristics based upon that information. 与原始游戏有8条获胜线不同,4x4x4版本具有(IIRC)76。如果有人将每条线都记录为8个州之一(十个获胜者为十个),并且对于每个空位,一个人都记录为有多少条获胜线通过了哪些州,应该有可能根据该信息制定一些非常快速的启发式方法。 It would probably be necessary to use an exhaustive search algorithm to ensure that heuristics would in fact win, but once the heuristics were validated they should be able to run much faster than would an exhaustive search. 可能有必要使用穷举搜索算法以确保启发式方法实际上会获胜,但是一旦验证了启发式方法,它们应比穷举搜索运行得快得多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM