問題來源於口試公司,先看看問題吧
假定我們如今有一個 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 [ ]
末了是加分項:
- 代碼可讀性高
- 代碼量少
- 機能高
- 有可運轉的單元測試
- 運用 ES6 語法完成
- 運用 functional programming
下面是本身的代碼完成.
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);
}
}();
寫的不好, 請多指教.