所有可能的Tic Tac Toe获胜组合

时间:2015-02-25 06:04:26

标签: algorithm combinations graph-algorithm

我接受了一次采访,我被问到一个看似简单的算法问题:“编写一个算法,将所有可能的获胜组合归还给我。”我仍然无法找到一种有效的方法来处理这个问题。是否有一个标准算法或常见应该应用于我不知道的类似问题?

6 个答案:

答案 0 :(得分:7)

这是一个对蛮力来说实际上非常简单的问题之一,虽然你可以使用组合学,图论或许多其他复杂的工具来解决它,但我是; d实际上会被申请人留下深刻印象,他们认识到这样的事实更容易(至少对于这个问题)。

在网格中只放置了3个 9 或19,683种可能的xo<blank>组合,并非所有这些组合都有效

首先,有效的游戏位置是xo计数之间的差异不超过1的位置,因为它们必须交替移动。

此外,两个方面都有三个连续状态是不可能的,所以它们也可以打折。如果两者都连续三个,那么其中一个将在之前的移动中获胜。

实际上还存在另一个限制,即如果没有共同的单元,一方可能以两种不同的方式赢得胜利(再次,他们会在之前的移动中获胜),这意味着: / p>

XXX
OOO
XXX

无法实现,而:

XXX
OOX
OOX

可以。但是我们实际上可以忽略这一点,因为如果没有一个共同的单元格就没有办法赢得两种方式而没有违反一个&#34;最大差异的一个&#34;规则,因为你需要六个单元格,而对手只有三个单元格。

所以我只是使用蛮力,对于每个位置,其中差异为零或计数之间的一个,检查双方的八个获胜可能性。假设只有其中一人获胜,那就是一场合法的胜利游戏。


下面是Python中的概念证明,但首先是time的输出在运行时将输出发送到/dev/null以显示它的速度:

real    0m0.169s
user    0m0.109s
sys     0m0.030s

代码:

def won(c, n):
  if c[0] == n and c[1] == n and c[2] == n: return 1
  if c[3] == n and c[4] == n and c[5] == n: return 1
  if c[6] == n and c[7] == n and c[8] == n: return 1

  if c[0] == n and c[3] == n and c[6] == n: return 1
  if c[1] == n and c[4] == n and c[7] == n: return 1
  if c[2] == n and c[5] == n and c[8] == n: return 1

  if c[0] == n and c[4] == n and c[8] == n: return 1
  if c[2] == n and c[4] == n and c[6] == n: return 1

  return 0

pc = [' ', 'x', 'o']
c = [0] * 9
for c[0] in range (3):
  for c[1] in range (3):
    for c[2] in range (3):
      for c[3] in range (3):
        for c[4] in range (3):
          for c[5] in range (3):
            for c[6] in range (3):
              for c[7] in range (3):
                for c[8] in range (3):
                  countx = sum([1 for x in c if x == 1])
                  county = sum([1 for x in c if x == 2])
                  if abs(countx-county) < 2:
                    if won(c,1) + won(c,2) == 1:
                      print " %s | %s | %s" % (pc[c[0]],pc[c[1]],pc[c[2]])
                      print "---+---+---"
                      print " %s | %s | %s" % (pc[c[3]],pc[c[4]],pc[c[5]])
                      print "---+---+---"
                      print " %s | %s | %s" % (pc[c[6]],pc[c[7]],pc[c[8]])
                      print

正如一位评论者指出的那样,还有一个限制因素。一个特定董事会的赢家不能拥有比输家更少的单元,因为这意味着失败者刚刚移动,尽管胜利者已经在最后一步获胜了。

我不会更改代码以考虑到这一点,但这将是一个简单的问题,即检查谁拥有最多的单元格(最后一个移动的人)并确保获胜线属于他们。

答案 1 :(得分:1)

另一种方式可以从八个获胜位置中的每一个开始,

xxx ---
--- xxx
--- --- ... etc.,

并递归填写所有合法组合(从插入2 o开始,然后为每个x添加o;避免o获胜位置):

xxx xxx xxx
oo- oox oox
--- o-- oox ... etc.,

答案 2 :(得分:0)

这是Java等效代码示例

package testit;

公共类TicTacToe {

public static void main(String[] args) {
    // TODO Auto-generated method stub
    // 0 1 2
    // 3 4 5
    // 6 7 8
    char[] pc = {' ' ,'o', 'x' };

    char[] c = new char[9];

    // initialize c

    for (int i = 0; i < 9; i++)
        c[i] = pc[0];

    for (int i = 0; i < 3; i++) {
        c[0] = pc[i];
        for (int j = 0; j < 3; j++) {
            c[1] = pc[j];
            for (int k = 0; k < 3; k++) {
                c[2] = pc[k];
                for (int l = 0; l < 3; l++) {
                    c[3] = pc[l];
                    for (int m = 0; m < 3; m++) {
                        c[4] = pc[m];
                        for (int n = 0; n < 3; n++) {
                            c[5] = pc[n];
                            for (int o = 0; o < 3; o++) {
                                c[6] = pc[o];
                                for (int p = 0; p < 3; p++) {
                                    c[7] = pc[p];
                                    for (int q = 0; q < 3; q++) {
                                        c[8] = pc[q];

                                        int countx = 0;
                                        int county = 0;

                                        for(int r = 0 ; r<9 ; r++){
                                            if(c[r] == 'x'){

                                                countx = countx + 1;
                                            }
                                            else if(c[r] == 'o'){

                                                county = county + 1;

                                            }


                                        }

                                        if(Math.abs(countx - county) < 2){

                                            if(won(c, pc[2])+won(c, pc[1]) == 1 ){
                                                System.out.println(c[0] + " " + c[1] + " " + c[2]);
                                                System.out.println(c[3] + " " + c[4] + " " + c[5]);
                                                System.out.println(c[6] + " " + c[7] + " " + c[8]);

                                                System.out.println("*******************************************");


                                            }


                                        }









                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

public static int won(char[] c, char n) {

    if ((c[0] == n) && (c[1] == n) && (c[2] == n))
        return 1;
    else if ((c[3] == n) && (c[4] == n) && (c[5] == n))
        return 1;
    else if ((c[6] == n) && (c[7] == n) && (c[8] == n))
        return 1;
    else if ((c[0] == n) && (c[3] == n) && (c[6] == n))
        return 1;
    else if ((c[1] == n) && (c[4] == n) && (c[7] == n))
        return 1;
    else if ((c[2] == n) && (c[5] == n) && (c[8] == n))
        return 1;
    else if ((c[0] == n) && (c[4] == n) && (c[8] == n))
        return 1;
    else if ((c[2] == n) && (c[4] == n) && (c[6] == n))
        return 1;
    else
        return 0;

}

} `

答案 3 :(得分:0)

今天,我接受了Apple的采访,我也有同样的问题。那一刻我不能好好想。后来,在参加会议之前,我在15分钟内编写了组合功能,当我从会议回来时,在15分钟内再次编写了验证功能。我对面试感到紧张,苹果不信任我的简历,他们只相信他们在面试中看到的东西,我不怪他们,很多公司都是一样的,我只是说在招聘过程中有些事情看起来不太聪明

无论如何,这是我在Swift 4中的解决方案,其中有8行代码用于组合功能,而17行代码用于检查有效的电路板。

干杯!

this.http.post("http://localhost:3003/login/authenticate", this.authenticateObj)
    .subscribe(
        (res: Response) => {
            // code when no error...
        }, 
        err => {
            // error handling...
        },
        () => {
            // finally...
        }
    ); 

答案 4 :(得分:0)

以下解决方案使用递归生成所有可能的组合

它消除了不可能的组合并返回了888个组合

以下是有效代码Possible winning combinations of the TIC TAC TOE game

const players = ['X', 'O'];
let gameBoard = Array.from({ length: 9 });

const winningCombination = [
  [ 0, 1, 2 ],
  [ 3, 4, 5 ],
  [ 6, 7, 8 ],
  [ 0, 3, 6 ],
  [ 1, 4, 7 ],
  [ 2, 5, 8 ],
  [ 0, 4, 8 ],
  [ 2, 4, 6 ],
];

const isWinningCombination = (board)=> {
  if((Math.abs(board.filter(a => a === players[0]).length - 
  board.filter(a => a === players[1]).length)) > 1) {
    return false
  }
  let winningComb = 0;
  players.forEach( player => {
    winningCombination.forEach( combinations => {
      if (combinations.every(combination => board[combination] === player )) {
        winningComb++;
      }
    });
  });
  return winningComb === 1;
}

const getCombinations = (board) => {
  let currentBoard = [...board];
  const firstEmptySquare = board.indexOf(undefined)
  if (firstEmptySquare === -1) {
    return isWinningCombination(board) ? [board] : [];
  } else {
    return [...players, ''].reduce((prev, next) => {
      currentBoard[firstEmptySquare] = next;
      if(next !== '' && board.filter(a => a === next).length > (gameBoard.length / players.length)) {
        return [...prev]
      }
      return [board, ...prev, ...getCombinations(currentBoard)]
    }, [])

  }
}

const startApp = () => {
  let combination = getCombinations(gameBoard).filter(board => 
      board.every(item => !(item === undefined)) && isWinningCombination(board)
    )
  printCombination(combination)
}

const printCombination = (combination)=> {
  const ulElement = document.querySelector('.combinations');
  combination.forEach(comb => {
    let node = document.createElement("li");
    let nodePre = document.createElement("pre");
    let textnode = document.createTextNode(JSON.stringify(comb));
    nodePre.appendChild(textnode);
    node.appendChild(nodePre); 
    ulElement.appendChild(node);
  })
}
startApp();

答案 5 :(得分:-1)

可以用蛮力解决,但请记住,当玩家 1 获胜时玩家 2 无法移动,反之亦然。还要记住 player1 和 player 的移动之间的差异不能大于 1 且小于 0。

我已经编写了验证提供的组合是否有效的代码,可能很快会发布在 github 上。