我最近在Objective-C中构建了一个Tic Tac Toe游戏,同时专注于尽可能地使我的代码尽可能简洁(因为我对编程相对较新,这是我第一次专注于提高效率) /可读而不是让它不惜任何代价工作)
在大多数情况下,我能够使我的代码简短而有效,以至于我对它感到满意 - 除了检查X或O是否赢得游戏的checkWin函数部分
我有一个名为“BoardState”的NSArray对象,索引0-8对应板上的9个点,如此
0 1 2
3 4 5
6 7 8
当索引设置为0时,它被视为空白。 1是X,2是O.
但是,我能弄清楚如何检查X或O是否赢了的唯一方法是用if语句手动检查整个板子并检查所有可能性,如此
if (
([_boardState[0] isEqual:@1] &&
[_boardState[1] isEqual:@1] &&
[_boardState[2] isEqual:@1]) ||
([_boardState[3] isEqual:@1] &&
[_boardState[4] isEqual:@1] &&
[_boardState[5] isEqual:@1]) ||
([_boardState[6] isEqual:@1] &&
[_boardState[7] isEqual:@1] &&
[_boardState[8] isEqual:@1]) ||
([_boardState[0] isEqual:@1] &&
[_boardState[3] isEqual:@1] &&
[_boardState[6] isEqual:@1]) ||
([_boardState[1] isEqual:@1] &&
[_boardState[4] isEqual:@1] &&
[_boardState[7] isEqual:@1]) ||
([_boardState[2] isEqual:@1] &&
[_boardState[5] isEqual:@1] &&
[_boardState[8] isEqual:@1]) ||
([_boardState[0] isEqual:@1] &&
[_boardState[4] isEqual:@1] &&
[_boardState[8] isEqual:@1]) ||
([_boardState[2] isEqual:@1] &&
[_boardState[4] isEqual:@1] &&
[_boardState[6] isEqual:@1])
){
[xWins show];
[self overWriteBoardState];
}
然后再次,与O,几乎完全相同的事情。
if (
([_boardState[0] isEqual:@2] &&
[_boardState[1] isEqual:@2] &&
[_boardState[2] isEqual:@2]) ||
([_boardState[3] isEqual:@2] &&
[_boardState[4] isEqual:@2] &&
[_boardState[5] isEqual:@2]) ||
([_boardState[6] isEqual:@2] &&
[_boardState[7] isEqual:@2] &&
[_boardState[8] isEqual:@2]) ||
([_boardState[0] isEqual:@2] &&
[_boardState[3] isEqual:@2] &&
[_boardState[6] isEqual:@2]) ||
([_boardState[1] isEqual:@2] &&
[_boardState[4] isEqual:@2] &&
[_boardState[7] isEqual:@2]) ||
([_boardState[2] isEqual:@2] &&
[_boardState[5] isEqual:@2] &&
[_boardState[8] isEqual:@2]) ||
([_boardState[0] isEqual:@2] &&
[_boardState[4] isEqual:@2] &&
[_boardState[8] isEqual:@2]) ||
([_boardState[2] isEqual:@2] &&
[_boardState[4] isEqual:@2] &&
[_boardState[6] isEqual:@2])
){
[oWins show];
[self overWriteBoardState];
}
作为奖励,我的overWriteBoardState函数将电路板重置为无法再修改的空白平板。这也可以更有效率
-(void)overWriteBoardState {
self.boardState[0] = @3;
self.boardState[1] = @3;
self.boardState[2] = @3;
self.boardState[3] = @3;
self.boardState[4] = @3;
self.boardState[5] = @3;
self.boardState[6] = @3;
self.boardState[7] = @3;
self.boardState[8] = @3;
}
我还能如何构建这个以避免这些大量的重复行?任何帮助或提示表示赞赏 - 谢谢!
答案 0 :(得分:2)
首先是数据存储。鉴于您正在处理一个小的固定大小的三态值数组,我建议使用uint8_t
,0
和1
使用2
的简单C数组。您已经提到的编码,而不是NSArray
,因为Objective-C集合类只能存储对象。
然后,你需要研究每个索引模式中关于行和行的关系,并迭代基值。
例如:
//
// Keep this in your comments, as it's invaluable:
//
// 0 1 2
// 3 4 5
// 6 7 8
//
BOOL didWinAcross(const uint8_t *board, uint8_t side)
{
BOOL didWin = NO;
for (unsigned row = 0; row < 3 && !didWin; row++) {
didWin =
board[row * 3 + 0] == side &&
board[row * 3 + 1] == side &&
board[row * 3 + 2] == side;
}
return didWin;
}
BOOL didWinDown(const uint8_t *board, uint8_t side)
{
BOOL didWin = NO;
for (unsigned column = 0; column < 3 && !didWin; column++) {
didWin =
board[column + 0] == side &&
board[column + 3] == side &&
board[column + 6] == side;
}
return didWin;
}
BOOL didWinDiagonal(const uint8_t *board, uint8_t side)
{
return (
board[0] == side &&
board[4] == side &&
board[8] == side) || (
board[2] == side &&
board[4] == side &&
board[6] == side);
}
然后使用这样的函数:
uint8_t board[9] = ...;
uint8_t sideThatJustMoved = 1;
if (didWinAcross(board, sideThatJustMoved) ||
didWinDown(board, sideThatJustMoved) ||
didWinDiagonal(board, sideThatJustMoved)) {
someoneWon(sideThatJustMoved);
}
答案 1 :(得分:1)
首先,我建议使用0
作为空方格,因为它会简化条件。
但基本上,不是经历每一个组合,而是经历组合模式。
- (NSUInteger) winnerStartingAt:(NSUInteger)index withStride:(NSUInteger)stride {
NSUInteger a = self.boardState[index] ;
NSUInteger b = self.boardState[index+stride] ;
NSUInteger c = self.boardState[index+2*stride] ;
if ( a && a==b && b==c ) // if they're all the same and they're all not empty
return a ;
return 0 ; // no winner (non-homogeneous list)
}
- (void) checkBoard {
NSUInteger winner = 0 ; // assume there's no winner
// check all the rows, which have a stride of 1
if(!winner) winner = [self winnerStartAt:0 withStride:1] ;
if(!winner) winner = [self winnerStartAt:3 withStride:1] ;
if(!winner) winner = [self winnerStartAt:6 withStride:1] ;
// check all the columns, which have a stride of 3 (they go downward)
if(!winner) winner = [self winnerStartAt:0 withStride:3] ;
if(!winner) winner = [self winnerStartAt:1 withStride:3] ;
if(!winner) winner = [self winnerStartAt:2 withStride:3] ;
// check the diagonals
if(!winner) winner = [self winnerStartAt:0 withStride:4] ;
if(!winner) winner = [self winnerStartAt:2 withStride:2] ;
if ( winner )
// whatever you want to do
}
我建议遵循一些其他建议,即使用2D整数数组而不是一维对象数组。这将带来性能优势。在你的水平和Tic Tac Toe,差异可以忽略不计,但我建议你学习下一件事,这样你就知道它是如何完成的,并且知道它们的性能差异。
答案 2 :(得分:0)
使用循环。 overWriteBoardState
方法可以写成:
- (void)overWriteBoardState {
for (NSInteger i = 0; i < 9; i++) {
self.boardState[i] = @3;
}
}
使用循环可以将X或O赢得的两个大if
语句转换为一个if
语句:
for (NSInteger p = 1; p <= 2; p++) {
if (
([_boardState[0] isEqual:@(p)] &&
[_boardState[1] isEqual:@(p)] &&
[_boardState[2] isEqual:@(p)]) ||
([_boardState[3] isEqual:@(p)] &&
[_boardState[4] isEqual:@(p)] &&
[_boardState[5] isEqual:@(p)]) ||
([_boardState[6] isEqual:@(p)] &&
[_boardState[7] isEqual:@(p)] &&
[_boardState[8] isEqual:@(p)]) ||
([_boardState[0] isEqual:@(p)] &&
[_boardState[3] isEqual:@(p)] &&
[_boardState[6] isEqual:@(p)]) ||
([_boardState[1] isEqual:@(p)] &&
[_boardState[4] isEqual:@(p)] &&
[_boardState[7] isEqual:@(p)]) ||
([_boardState[2] isEqual:@(p)] &&
[_boardState[5] isEqual:@(p)] &&
[_boardState[8] isEqual:@(p)]) ||
([_boardState[0] isEqual:@(p)] &&
[_boardState[4] isEqual:@(p)] &&
[_boardState[8] isEqual:@(p)]) ||
([_boardState[2] isEqual:@(p)] &&
[_boardState[4] isEqual:@(p)] &&
[_boardState[6] isEqual:@(p)])
) {
if (p == 1) {
[xWinds show];
} else {
[oWins show];
}
[self overWriteBoardState];
}
}
通过一些循环和一些其他表示匹配的数组数据,可以将大if
语句转换为更简单的形式,但是大if
语句实际上可能更简单,更清晰。
答案 3 :(得分:0)
列出获胜组合,浏览列表以返回获胜者(1或2)。
- (int)checkWinnerForBoard:(NSArray*)_boardState
{
NSArray *tests = @[@[@0, @1, @2], @[@3, @4, @5], @[@6, @7, @8], @[@0, @3, @6], @[@1, @4, @7], @[@2, @5, @8], @[@0, @4, @8], @[@2, @4, @6]];
for (NSArray *test in tests) {
int value = [_boardState[[test[0] intValue]] intValue];
if (value && value == [_boardState[[test[1] intValue]] intValue] && value == [_boardState[[test[2] intValue]] intValue])
return value;
}
return 0;
}
您可以将组合数组存储在-init
中,这样您就不会每次都重新创建它。
或者,您可能只想测试最后一个位置。
- (int)checkWinnerForBoard:(NSArray*)_boardState lastposition:(int)lastPosition
{
NSArray *tests = @[@[@[@1, @2], @[@3, @6], @[@4, @8]], @[@[@0, @2], @[@4, @7]], @[@[@0, @1], @[@5, @8], @[@4, @6]], @[@[@0, @6], @[@4, @5]], @[@[@3, @5], @[@1, @7], @[@0, @8], @[@2, @6]], @[@[@3, @4], @[@2, @8]], @[@[@0, @3], @[@2, @4], @[@7, @8]], @[@[@1, @4], @[@6, @8]], @[@[@2, @5], @[@6, @7], @[@0, @4]]];
for (NSArray *test in tests[lastPosition]) {
int value = [_boardState[lastPosition] intValue];
if (value == [_boardState[[test[0] intValue]] intValue] && value == [_boardState[[test[1] intValue]] intValue])
return value;
}
return 0;
}
同样,您可以将组合数组存储在-init
中,这样您就不会每次都重新创建它。