我正在开发一个井字游戏,旨在学习不同的算法及其实现方法,并为计算机对手设计不同的AI实现。首先尝试的是最简单的实现方式,即让计算机每次随机选择一个空格。
这种方法在一定程度上是有效的,但问题在于运行时间。每当调用aiRandMove()
方法时,选择移动所需的时间越来越长,以至于在棋盘上进行了5次移动(计算机和用户的总和)后,程序似乎会卡住(尽管技术上并非如此)。
经过进一步的调试,我意识到这是可以预期的,因为aiRandMove()
方法随机选择X和Y坐标,然后测试该移动是否合法。随着可用空间越来越少,合法的移动也越来越少,因此随机生成器尝试生成合法移动的失败次数越来越多。
我的问题是,有没有办法修改这个函数,至少能减少函数执行的时间?据我从谷歌搜索和自己分析问题所知,我无法想到在不牺牲函数“随机性”的情况下优化它的方法。我考虑过保留计算机尝试过的移动数组,但这并不能解决问题,因为这不会影响rand()
生成重复数字的次数。以下是与此问题相关的函数代码:
//处理AI随机移动的函数,需要一个棋盘对象来测试移动的合法性和一个玩家对象来执行移动
//两者都通过引用传递,因为需要保存棋盘状态和玩家的评估数组的变化
char aiRandMove(Player &ai, Board &game){
int tryX;
int tryY; //存储计算机尝试的移动的变量
bool moveMade = false;
char winner;
while(!moveMade){
srand(time(NULL));//为rand()随机化种子
tryX = rand() % 3;
tryY = rand() % 3; //坐标是X和Y之间的随机数
cout << "尝试移动 " << tryX << ", " << tryY << endl;
if(game.isLegalMove(tryX, tryY)){
winner = game.makeMove(tryX, tryY, ai);
moveMade = true;
}
}
return winner;
}
我还尝试将种子函数移出while循环(最初将其放入while循环中是为了“增加随机性”,尽管这在逻辑上是错误的,这也没有改善结果)。
如果一切都失败了,我可能会将这个方法标记为“简单”,并且只进行随机移动,直到我能判断是否需要阻止或进行获胜移动。但也许还有其他随机函数可以帮助解决这个问题。任何想法和评论都非常欢迎!
回答:
你需要从方程中移除无效的移动,例如使用以下伪代码,使用数组收集有效的移动:
possibleMoves = []for each move in allMoves: if move is valid: add move to possibleMovesmove = possibleMoves[random (possibleMoves.length)]
这样可以避免每次尝试移动时多次调用随机函数,因为数组中的所有可能性都是有效的。
另一种方法是从possibleMoves
数组开始游戏,并在使用后移除每个可能性。
你还需要了解,最好只对随机数生成器进行一次种子设置,然后只使用它生成的数字。每次尝试获取随机数时都用time(0)
进行种子设置,会确保在一整秒内得到相同的数字。