>_<:本次是综合以上的学习,然后加入博弈算法,来实现井字棋的外挂[井字棋代码可以在[软件项目]里找到!]
>_<:首先还是要加一个按钮:即[自动对战]button5
>_<:这里包括博弈算法的初始函数init(),根据游戏状态表更新博弈计算相关表的函数reup(),还有博弈主体部分getPos()来获得下棋位置,然后在button-5的监听里模拟鼠标点击。
>_<:这里简单说一下该算法里的博弈思想:
- win[2][8]分别保存计算机和外挂8种胜利情况的棋子数,如果值为5则表示此种情况不能胜利
- ptab[9][8]表示电脑第i号棋子在第j号胜利情况下是否有效
- ctab[9][8]表示外挂第i号棋子在第j号胜利情况下是否有效
- init()函数中主要将各组合状态的棋子数归零,将横竖对角线8种获胜情况置有效
- reup()函数中主要根据当前棋盘状态,更新每种组合状态的棋子数及电脑和外挂对应棋子在对应胜利情况的有效性更新
- getPos()函数中主要分别计算每个可以下棋的点对电脑和外挂的获胜权值来选择下棋的位置,注意这里相同情况下外挂的权值稍高
1 int chessdata[3][3];//a[y][x]棋盘状态0-1-2 2 int win[2][8];//PC和外挂在8种情况下的棋子数 3 bool ptab[9][8]; //电脑的获胜的状态表 4 bool ctab[9][8]; //外挂的获胜的状态表 5 6 void init() 7 { 8 int count=0,i,k; 9 //设定外挂与计算机在各个获胜组合中的棋子数 10 for(i=0;i<8;i++) 11 { 12 win[0][i]=0; 13 win[1][i]=0; 14 } 15 16 17 //设定水平方向的获胜组合 18 for(i=0;i<=6;i+=3) 19 { 20 for(k=0;k<3;k++)//3个棋子1个获胜组合 21 { 22 ptab[i+k][count]=true; 23 ctab[i+k][count]=true; 24 } 25 count++; 26 } 27 //设定垂直方向的获胜组合 28 for(k=0;k<3;k++) 29 { 30 for(i=0;i<=6;i+=3)//3个棋子1个获胜组合 31 { 32 ptab[i+k][count]=true; 33 ctab[i+k][count]=true; 34 } 35 count++; 36 } 37 //设定对角线方向上的获胜组合 38 for(i=2;i<=6;i+=2){ 39 ptab[i][count]=true; 40 ctab[i][count]=true; 41 }count++; 42 for(i=0;i<=8;i+=4){ 43 ptab[i][count]=true; 44 ctab[i][count]=true; 45 } 46 } 47 void reup()//根据棋盘状态更新胜利表、外挂、电脑表 48 { 49 for(int i=0;i<3;i++){ 50 for(int j=0;j<3;j++){ 51 if(chessdata[i][j]==2){ 52 //改变胜利表和各外挂、PC各胜利组合的棋子数 53 for(int k=0;k<8;k++){ 54 if(ptab[i*3+j][k]){ 55 win[0][k]++; 56 ctab[i*3+j][k]=false; 57 win[1][k]=5; 58 } 59 } 60 }else if(chessdata[i][j]==1){ 61 //改变胜利表和各外挂、PC各胜利组合的棋子数 62 for(int k=0;k<8;k++){ 63 if(ptab[i*3+j][k]){ 64 win[1][k]++; 65 ptab[i*3+j][k]=false; 66 win[0][k]=5; 67 } 68 } 69 } 70 } 71 } 72 } 73 int getPos()//获取该下棋位置 74 { 75 int grades[2][9]; 76 int m,i,max=0; 77 int u; 78 79 for(m=0;m<9;m++){ 80 grades[0][m]=0; 81 grades[1][m]=0; 82 83 if( chessdata[m/3][m%3]==0){ 84 for(i=0;i<8;i++){ 85 //计算PC在空棋格上的获胜分数 86 if(ptab[m][i] && win[0][i]!=5){ 87 switch(win[0][i]){ 88 case 0: 89 grades[0][m]+=1; 90 break; 91 case 1: 92 grades[0][m]+=2000; 93 break; 94 case 2: 95 grades[0][m]+=10000; 96 break; 97 } 98 } 99 100 //计算外挂在空格上的获胜分数 101 if(ctab[m][i] && win[1][i]!=5){ 102 switch(win[1][i]){ 103 case 0: 104 grades[1][m]+=1; 105 break; 106 case 1: 107 grades[1][m]+=2001; 108 break; 109 case 2: 110 grades[1][m]+=10001; 111 break; 112 } 113 } 114 } 115 116 if(max==0)u=m; 117 118 if(grades[0][m]>max){ 119 max=grades[0][m]; 120 u=m; 121 } 122 else if(grades[0][m]==max){ 123 if(grades[1][m]>grades[1][u])u=m; 124 } 125 126 if(grades[1][m]>max){ 127 max=grades[1][m]; 128 u=m; 129 } 130 else if(grades[1][m]==max){ 131 if(grades[0][m]>grades[0][u])u=m; 132 } 133 } 134 } 135 return u; 136 } 137 138 void CFewDlg::OnButton5() 139 { 140 HWND gameh=::FindWindow(NULL,"井字棋");//获取窗口句柄 141 //获取窗口进程ID 142 DWORD processid; 143 ::GetWindowThreadProcessId(gameh,&processid); 144 HANDLE processH=::OpenProcess(PROCESS_ALL_ACCESS,false,processid);//打开指定进程 145 146 //读指定进程 内存数据 147 DWORD byread; 148 LPCVOID pbase=(LPCVOID)0x00F5507C; //棋盘数据基址 149 LPVOID nbuffer=(LPVOID)&chessdata; //存放棋盘数据 150 ::ReadProcessMemory(processH,pbase,nbuffer,3*3*4,&byread);//进程句柄|基址|存放数据缓冲区|要读取数据的字节数|实际读取的字节数 151 152 //算法部分:自动走下一步 153 init(); 154 reup(); 155 int pos=getPos(); 156 157 //鼠标点击 158 int x=50+pos%3*100,y=50+pos/3*100; //定义座标点 159 int lparam; //定义座标点变量 160 lparam=(y<<16)+x; //表示指定格,Y<<16是左移16位,发消息用的Y座标点 161 ::SendMessage(gameh,WM_LBUTTONDOWN,0,lparam);//鼠标按下消息 162 ::SendMessage(gameh,WM_LBUTTONUP,0,lparam); //鼠标抬起消息 163 }