題目來源於面試公司,先看看題目吧面試
假設咱們如今有一個 3 x 3 的井字棋遊戲,咱們用一個二維數組表明棋盤,’x’ 表明玩家 X 下的棋子,’o’ 表明玩家 O 下的棋子,’e’ 表明該格沒有棋子。例如:一個空白的棋盤如下面的二維數組表示:
[[‘e’, ‘e’, ‘e’], [‘e’, ‘e’, ‘e’], [‘e’, ‘e’, ‘e’] ]
若是玩家 X 在第一行第一列下了一步棋,玩家 O 在第二行第二列下了一步棋,則表示以下:
[[‘x’, ‘e’, ‘e’], [‘e’, ‘o’, ‘e’], [‘e’, ‘e’, ‘e’]]
如今須要一個 function,接受一個已有任意棋子的棋盤(和上面二維數組同樣的格式),和玩家的標誌(’x’ 或 ‘o'),返回該玩家下一步有幾種可能的獲勝方式(獲勝方式以數組表示,[0, 0] 表明在第一行第一列下一步棋便可獲勝,[2, 2] 表明在第三行第三列下一步棋便可獲勝)。例如:
someFunction( ‘x’, [[‘o’, ‘e’, ‘e’], [‘o’, ‘x’, ‘o’], [‘x’, ‘x’, ‘e’]]) // return [ [2, 2], [0, 1], [0, 2] ] someFunction( ‘x’, [[‘x’, ‘o’, ‘o’], [‘x’, ‘x’, ‘e’], [‘e’, ‘o’, ‘e’]]) // return [[2, 2], [1, 2], [2, 0]] someFunction( ‘x’, [[‘x’, ‘x’, ‘o’], [‘e’, ‘e’, ‘e’], [‘e’, ‘e’, ‘e’]]) // return [ ] someFunction( ‘o’, [[‘o’, ‘o’, ‘o’], [‘e’, ‘e’, ‘e’], [‘e’, ‘e’, ‘e’]]) // return [ ]
最後是加分項:數組
下面是本身的代碼實現.性能
var getSuccess; +function(){ //假設3個相連便可獲勝. const linkLen = 3; //實際上, 咱們能夠理解爲, 圍繞某個沒有棋子的點,在 X 和 十 字這4個方向上的比較 //所以這裏提供獲取該4個方向上獲取棋盤點的簡易計算公式. const _getTraverlConfig = function(distance){ return [{ cStartRow : -1 * linkLen + 1, cStartLine : -1 * linkLen + 1, rowAdd : 1, lineAdd : 1, }, { cStartRow : -1 * linkLen + 1, cStartLine : linkLen - 1, rowAdd : 1, lineAdd : -1, }, { cStartRow : -1 * linkLen + 1, cStartLine : 0, rowAdd : 1, lineAdd : 0, }, { cStartRow : 0, cStartLine : -1 * linkLen + 1, rowAdd : 0, lineAdd : 1, }]; } //提供一個target字符串重複n次本身的方法 const _strRepeat = function (target, n){ var s = target, total = ''; while( n > 0 ) { if(n % 2 == 1) total += s; if(n == 1) break; s += s; n = n >>1 ; } return total; } //最終對外暴露的方法, 第一個參數是下棋的人, 第二個是棋盤的點數組. getSuccess = function( piece, chessboard ){ var availablePosition = [], distance = chessboard.length, traversalConfig, maxLen, compareStr, chessStr; //根據幾個點連成線獲取4個方向上的計算公式; traversalConfig = _getTraverlConfig(linkLen); //計算方向上的點數量. maxLen = 2 * (linkLen - 1) + 1; //若是傳入的piece爲x,那麼這裏的值就爲xxx compareStr = _strRepeat(piece, linkLen); //這一步很重要, 將棋盤轉爲一個字符串,爲的就是更快. chessStr = chessboard.reduce( (x,y) => x + y.join('') , '') //檢查每個字符串, 若是是空的話就判斷可否獲勝. for( let i = 0, len = chessStr.length; i < len; i++) if( 'e' == chessStr.charAt(i) ) _checkAvailable(i); function _checkAvailable( sqnm ){ //計算點在棋盤上的行和列. let curRow = Math.floor(sqnm / distance), curLine = sqnm % distance; //以傳入的參數填在該空點上,獲得一個新的棋盤字符串. let tempChessStr = chessStr.substring(0, sqnm) + piece + chessStr.substring(sqnm + 1, chessStr.length); //4個方向上的檢查 for( let i = 0, len = traversalConfig.length; i < len; i++) { let { cStartRow, cStartLine, rowAdd, lineAdd } = traversalConfig[i]; let tempStr = ''; let j = 0; let row = curRow + cStartRow; let line = curLine + cStartLine; while( j < maxLen ) { //超出邊界視爲空字符串. if(row < 0 || row > distance || line > distance || line < 0){ tempStr += ''; } else { //方向上棋盤的實際棋子的集合字符串. tempStr += tempChessStr.charAt(row * distance + line); } row += rowAdd; line += lineAdd; j++; } //若是在該字符串中含有須要比較的重複字符串便可認爲該點能夠獲勝. if( -1 != tempStr.indexOf(compareStr) ) { availablePosition.push([curRow, curLine]); return; } } } console.log(availablePosition); } }();
寫的很差, 請多指教.單元測試