下面是我写的一个tic-tac-toe的简单游戏。
我主要的思路是使用极大极小的搜索策略。并且体会到博弈程序的好坏很大程度上取决于局面评估函数的好坏。
因为机器方希望在所有下一步可以下子的集合中,选择最优的步法。这是就需要就局面进行评估,需要附加一些知识,而局面的评估值可以是所有这些知识评估的结果之和。
评估函数和程序对局面的搜索速度是成反比的。
我的程序中评估函数比较弱。
主要有二个类:
~~~~~~~~~~~~~~~~~~~~Map.h~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
#ifndef MAP_H
#define MAP_H
#include<iostream>
#include<cstring>
#define MAX_DEPTH 2
using namespace std;
class Map{
public :
Map(){
}
//the computer try to make best movement
int makeBestMove(Player& computer){
int bestValue = minMaxSearch(MAX_DEPTH , computer.type );
cout<<"Computer (x,y):"<< bestMove.x <<" "<<bestMove.y<<endl;
makeMove(computer.type , bestMove.x , bestMove.y );
return 0;
}
//check wether the board is all occupied
bool isAllOccupied(){
bool result = true ;
for(int i=1;i<=boardSize; i++){
for(int j=1 ; j<=boardSize ; j++){
if( '0' == board[i][j]){
result = false ;
return result ;
}
}
}
return result ;
}
//decide wether one side has won
bool isHuamanOrComputerWin(const int& side){
bool win = false ;
char flag = (side == 1)? 'c' :'h';
int i;
for( i=1 ; i<=boardSize ; i++){ //行
if( flag== board[i][0] && flag == board[i][1] && flag == board[i][2] ){
win = true ;
return win ;
}
}
for (i =1 ; i<=boardSize ; i++){ //列
if( flag == board[0][i] && flag == board[1][i] && flag == board[2][i] ){
win = true ;
return win;
}
}
if( ((board[1][1] == board[2][2]) && (board[1][1] == board[3][3])) ||
((board[3][1] == board[2][2]) && (board[3][1] == board[1][3])) )
{
win = true ;
return win ;
}
return win ;
}
// 1:computer -1:human 'c': computer 'h':human
bool makeMove(const int& side,const int& x,const int& y ){
if( x<=0 || x >=4 || y<=0 || y>=4 || board[x][y] != '0')
return false ;
board[x][y]= (1 == side )? 'c' : 'h' ;
return true ;
}
//umake previous movement
bool unmakeMove(const int& x,const int& y){
if( x<=0 || x >=4 || y<=0 || y>=4 || board[x][y] == '0')
return false ;
board[x][y] = '0';
return true ;
}
//print the board
void printBoard(){
for( int i=1 ; i<= boardSize ; i++){
for(int j=1 ; j<= boardSize ; j++){
cout<<board[i][j]<<" ";
}
cout<<endl;
}
}
//max-min search strategy
int minMaxSearch(int depth , int side ){
int value,best;
Move moveArray[9]; //保存可下子的位置
if( 1== side )
best = INT_MIN ;
if( -1 == side )
best = INT_MAX ;
if( 0 == depth ){
return evaluateMap();
}
int num = genAllMove(moveArray) ;
for(int i=0; i<num ; i++){
makeMove(side, moveArray[i].x , moveArray[i].y );
value = minMaxSearch(depth -1 ,-1 * side );
unmakeMove(moveArray[i].x , moveArray[i].y );
if( 1 == side ){
if( value > best){
best = value ;
if( MAX_DEPTH == depth )
bestMove = moveArray[i];
}
}
if( -1 == side ){
if( value < best ){
best = value ;
if(MAX_DEPTH == depth )
bestMove = moveArray[i];
}
}
}
return best ; //返回走棋方的最优值
}
private:
static char board[4][4];
enum { boardSize=3 };
typedef struct move{
int x;
int y;
}Move ;
Move bestMove; //最佳的下子位置
int genAllMove(Move* array){ //这里Move的定义要放在对Move的引用之前
int moveNum = 0;
for(int i=1 ; i<=boardSize ; i++){
for(int j=1 ; j<=boardSize ;j++){
if( '0' == board[i][j]){
array[moveNum].x= i;
array[moveNum].y = j;
moveNum ++;
}
}
}
return moveNum;
}
int evaluateMap(){
char boardCopy[4][4];
memcpy( boardCopy , board , sizeof(board)); //对原来的board进行备份
int comp=0,human=0;
for( int i=1 ;i<=boardSize ; i++){
for(int j=1 ; j<=boardSize ; j++){
if( 'c' == board[i][j]){
comp +=util(i,j,'c');
}
if( 'h' == board[i][j] ){
human += util(i,j,'h');
}
}
}
memcpy( board , boardCopy , sizeof(boardCopy)); //还原原来的board
return comp - human ;
}
int util(int x,int y,char c){
int score= 0 , i , j ;
for(int k=0; k<8 ; k++) {
i = x+ dir[k][0] ; //2连子
j = y+ dir[k][1] ;
if( i<=0 || i>=4 || j<=0 || j>=4 || c != board[i][j] )
continue;
score +=4 ;
i = i+ dir[k][0] ; //朝相同方向再测试
j = j+ dir[k][1] ;
if( i<=0 || i>=4 || j<=0 || j>=4 || c != board[i][j] )
continue;
score -= 4;
score += 16 ;
}
board[x][y] = '0' ;
if( 0 == score )
return 1 ;
else
return score ;
}
const static int dir[8][2];
};
#endif
char Map::board[4][4]={
'0','0','0','0',
'0','0','0','0',
'0','0','0','0',
'0','0','0','0'
};
const int Map::dir[8][2]={ {0 ,-1},{-1,0},{ 0 , 1} ,{ 1,0},{-1,-1},{-1,1},{1,-1},{1,1} };
~~~~~~~~~~~~~~~~~~~~~~~Player.h~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#ifndef PLAYER_H
#define PLAYER_H
class Player{
public:
Player(int t):type(t){
}
public:
int posx; //记录player当前的位置
int posy;
int type;
};
#endif
~~~~~~~~~~~~~~~~~~~~~~~主程序~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include<iostream>
#include "Player.h"
#include "Map.h"
#define NOT !
using namespace std;
Player computer(1),human(-1); // 1对应computer -1对应human
Map map;
int main(){
cout<<"tic-tac-toe Game!"<<endl<<endl;
cout<<"Computer make first step...." <<endl;
computer.posx = 2;
computer.posy = 2;
map.makeMove(computer.type,computer.posx,computer.posy);
map.printBoard();
bool moveCorrectFlag = true ;
//start the game
while( NOT map.isAllOccupied() ){
cout<<"make step (x,y):";
cin>>human.posx>>human.posy ;
moveCorrectFlag = map.makeMove(human.type , human.posx ,human.posy );
if( false == moveCorrectFlag ){
cout<<"invalid move.. try again!"<<endl;
continue ;
}
else{
map.printBoard();
if( map.isHuamanOrComputerWin(human.type)){ //if human win
cout<<"You Win!"<<endl;
return 0;
}
else{
map.makeBestMove(computer) ;
map.printBoard();
if( map.isHuamanOrComputerWin(computer.type) ){
cout<<"You Lost!"<<endl;
return 0;
}
}
}
}
cout<<"A Draw ... The Game Ends!"<<endl;
return 0;
}