以下是我对一个问题的解决办法。虽然它有效--我想学习如何以更好的方式优化它(我是C++新手)。我最初的想法是使用一个map来映射r,c对,然后测试它们,但是希望有人能帮我优化这个。
使用一个字符串网格和两个行/列对(r1,c1) (r2,c2)编写一个函数,如果棋盘广场上有一个骑士(r1,c1)可以移动到空方块(r2,c2),则返回true。要使函数返回true,必须有一个正方形骑士(r1,c1),平方at (r2,c2)必须存储一个空字符串,这两个位置都必须在网格的范围内。一位骑士做出了一个"L“形状的动作,其中一个维度上有两个正方形,另一个维度中有一个正方形。也就是说,如果棋盘(1,2)存储“骑士”,那么knightMoves( board,1,2,2,4)的调用返回true。
这是我目前的解决方案
bool knightMoves(Grid<string>& board, int r1, int c1, int r2, int c2);
int main() {
//create a fake board
Grid<string> board(7,7);
board[1][2] = "knight";
board[3][1] = "rock";
board[0][4] = "king";
//fire the function
bool result = knightCanMove(board, 1, 2, 2, 4);
cout << result << endl;
return 0;
}
bool knightMoves(Grid<string>& board, int r1, int c1, int r2, int c2) {
bool canMove = false;
string knight = "knight";
//within grid bounds
if(r1 > board.numRows() || c1 > board.numCols()) return false;
if(r2 > board.numRows() || c2 > board.numCols()) return false;
//test whether the co-ords match a knight string,
//if yes, continue
if (board[r1][c1] == knight) {
if(board[r1 + 2][c1 + 1] == "" && r1+2 == r2 && c1+1 == c2) {
canMove = true;
}
if(board[r1 + 2][c1 - 1] == "" && r1+2 == r2 && c1-1 == c2) {
canMove = true;
}
if(r1 >= 2 && board[r1 - 2][c1 + 1] == "" && r1-2 == r2 && c1+1 == c2) {
canMove = true;
}
if(r1 >= 2 && board[r1 - 2][c1 - 1] == "" && r1-2 == r2 && c1-1 == c2) {
canMove = true;
}
if(board[r1 + 1][c1 + 2] == "" && r1+1 == r2 && c1+2 == c2) {
canMove = true;
}
if(c1 >= 2 && board[r1 + 1][c1 - 2] == "" && r1+1 == r2 && c1-2 == c2) {
canMove = true;
}
if(board[r1 - 1][c1 + 2] == "" && r1-1 == r2 && c1+2 == c2) {
canMove = true;
}
if(c1 >= 2 && board[r1 - 1][c1 - 2] == "" && r1-1 == r2 && c1-2 == c2) {
canMove = true;
}
}
return canMove;
}发布于 2017-10-07 23:46:23
看来在范围检查方面可能有问题。
< 0 )类型没有标记为无符号,所以它们可能是负的。numXYZ应该包括边界.( >= board.numXYZ() )范围检查如下:
if ( r1 < 0 || r1 >= board.numRows() ||
r2 < 0 || r2 >= board.numRows() ||
c1 < 0 || c1 >= board.numCols() ||
c2 < 0 || c2 >= board.numCols() ) {
return false;
}跳转检查可以简化。一个维度的绝对距离必须是一个,而另一个维度的绝对距离必须是两个。
unsigned int rowdiff = abs(r1 - r2);
unsigned int coldiff = abs(c1 - c2);
if ( rowdiff == 1 && coldiff == 2 || rowdiff == 2 && coldiff == 1 )或者,我们可以注意到,距离的乘积必须是±2,从而导致更简洁的检查,但这可能会模糊根本的目标:
if ( abs((r1 - r2) * (c1 - c2)) != 2 ) return false;在这一点上,最后的检查是:
return board[r1][c1] == "knight" && board[r2][c2] == "";发布于 2017-10-08 01:14:16
rock,而是Rook。很抱歉吹毛求疵,我是个棋手。unsigned。发布于 2017-10-08 05:16:37
我同意另外两个答案。他们有很好的建议。我想指出的是,您的代码有一些更大的问题。
在代码中有几个重要字符串。通过分配一个变量来保存字符串knightMoves(),您在"knight"函数中有了正确的想法。(同时,这个函数的名字也不错!不知道你是不是鲍勃·塞格的粉丝,但这让我笑了起来。)如果要使用字符串来表示所有棋子,则应该将它们定义为命名常量,并且只键入它们一次,这样就不会在某个地方出错。类似于:
const std::string kKing = "king";
const std::string kQueen = "queen";
//... etc.不过,还有更好的办法。
您的代码就是我们所称的粗壮打字。这是一个应该避免的反模式。
C++是一种强类型(而不是典型的)语言。也就是说,它允许您为数据创建类型,如果使用不当,可以给出警告或错误。通过使您的board成为一个Grid<string>,任何东西都可以进入它,编译器将无法告诉您它是否是一个错误。例如,与“国王”不同,您可能会意外地将一个"knig“放到板上,而您的代码的其余部分可能会被这一点搞糊涂,并做一些意想不到的事情。(或者你也可以把一只“青蛙”、“挪威”或“埃尔玛·庞贝克”也放进董事会。)
相反,您应该使用命名类型。在这种情况下,您应该使用枚举类型。类似于:
typedef enum ChessPiece {
kNone,
kPawn,
kRook,
kBishop,
kKnight,
kKing,
kQueen
} ChessPiece;然后你可以让你的板成为一个Grid<ChessPiece>,如果你输入了一个错误的片段,那么编译器会在它发生时告诉你。
https://codereview.stackexchange.com/questions/177432
复制相似问题