题目来源于面试公司,先看看题目吧
假设我们现在有一个 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);
}
}();
写的不好, 请多指教.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。