主题:[原创]自己写的一个五子棋小程序
电脑下棋时是根据棋形来计算棋盘上每一个格子的分数,就下在分数最高的一个方格上面。棋盘有三种状态,有黑棋(black),有白棋(white),没有放棋(blank)。首先,有放棋的地方(!blank),分数为零,以保正不会放在这个地方。按照我们下五子棋的习惯,相同的棋子连得越多,附近那个空格越重要。所以,我采用的记分策略是这样的,用等比数列来定义每一个相同棋子的分数,倍率为3。先看一个例子。(空表示没有放棋,黑代表黑棋,白代表白棋)
空黑黑黑黑空
第一个黑棋算8分,第二个黑棋算8*3=24分,第三个黑棋算24*3=72分,第四个黑棋算72*3=216分,所以空格的的分数为 8+24+72+216=320。这样算,基本上连的棋越多,分数增加得越快。
另外,在看另一个例子,白空空黑黑黑空空空,按道理右边的第二个空格会不如第一个空格重要,这样我是这样处理的,左边第一个空格开始就从8分算起,跟着8*3,8*3*3。而右边第二个空格从5分算起,跟着5*3,5*3*3,右边第三个空格也从5分开始算起。这样黑棋最近的一个空格的分数一定会比不是附近的空格分数要高。
再看一种情况,1)空黑黑黑白,按道理那些黑棋有个白棋挡住,空格的分数不如,2)空黑黑黑空中空格的分数高。我这样算(下面用N来表示连棋的个数)。N=0, 从左到右扫描,第一个黑棋,N++, 第二个黑棋,N++, 第三个黑棋N++, 第四个不是黑棋,如果是白棋,N--, 停止扫描。 如果是空格,停止扫描。这样,情况1)N=2, 就是首项为8,项数为2,公比为3的等比数列求和。情况2)N=3, 就是首项为8,项数为3,公比为3的等比数列求和。还有,情况1) 的白棋换成边界,就是空黑黑黑边,和空黑黑黑白一样。
再看, 空黑黑黑黑白, 和空黑黑黑黑空的地位是一样的,反正是四个棋了,有没有白棋挡着也一样。这样,当N=4的时候,我让程序立即停止扫描,就是立即跳出循环。
注意,我举例用的是黑棋,其实黑棋白棋一样是这样算的嘛。
///////////////////////////////////////////////////////////////////////////////
上面说的是一个方向,五子棋是有八个方向的,就向不同的方向扫描啦,再将总分相加
如果分数相同会这么样呢,我是用一个向量将分数相同的左边存起来,然后一个随机函数来生成坐标,不然电脑老是下成一样,就没有意思了。
///////////////////////////////////////////////////////////////////////////////
差一点说漏了,有种情况是这样的 白空黑黑黑白 这样向右边扫描,N=2,但是左边有个白棋, 就从开始N=0, 先N--,就算到N=1。 如果 黑空黑黑黑白,左边有个棋相同的,就从开始N=0, N++, N=4时立即跳出,就得到N=4。基本上,如果一个方向上有N=4, 就会下在那个空格上面了。
/////////////////////////////////////////////////////////////////////////
我要说一下,我还不会编界面,所以不能够用鼠标来输入,我自己觉得要写个界面是不难的,难就难在那个策略。输入只能用坐标,输入方法为 1a, aa,之类。10之后的用字母来表示。先向下数,再向右数。反正,你自己编译一下,就会明白的了。我是用VC++6.0编译的。
///////////////////////////////////////////////////////////////////////
先不说了,也好难说明白的,自己看看代码吧,我想也好难以看明白的了。因为有些细节好难以表达。我英语不是好好,所以好多名称都取得不是好好,就好象输入我除了用output就不知道第二个单词了。我还专门用字典查过的。请不用见怪。
下面我也会解析一下的了
///////////////////////////////////////////////////////
#include <iostream>
#include <vector>
#include <ctime>
#include <iomanip>
#include <cmath>
#include <cctype>
using namespace std;
enum States{ blank, black, white }; //空白,黑棋,白棋三种状态
const int N=15; //15*15棋盘,可以改
unsigned int randSeed=time(0);
class myPoint
{
public:
myPoint() { x=1; y=1; };
myPoint( int a, int b) { x=a; y=b; };
int x;
int y;
};
/////////////////////////////////////////////
void Output( States A[][N] ); //输出棋盘
void initiChessMarks( int A[][N] ); //初始化分数
void updateMarks( int chessMarks[][N], const States chessStates[][N] );
bool isGameOver( myPoint pt, States chessStates[][N] );
int Explain();
bool isPermit( myPoint pt, States chessStates[][N] );
bool isPlayAgain();
myPoint maxMarkPoint( int chessMarks[][N] );
myPoint input();
void output( myPoint pt );
unsigned int random();
//==============================================
int main()
{
do
{
States chessStates[N][N]={blank}; //棋盘状态
int chessMarks[N][N]={0};//棋盘分数
int count=1;
myPoint tempA;
myPoint tempB;
int choice=Explain();
if( count%2!=choice )
Output( chessStates );
initiChessMarks( chessMarks);
do
{
if( count%2!=choice)
{
cout<<"请你下棋: ";
tempA=input();
tempB=tempA;
while( !isPermit(tempA,chessStates) )
{
cout<<"你下的棋步不对"<<endl;
cout<<"请你下棋: ";
tempA=input();
}
cout<<endl;
chessStates[tempA.x][tempA.y]=white;
updateMarks( chessMarks, chessStates );
}
else
{
tempA=maxMarkPoint( chessMarks );
chessStates[tempA.x][tempA.y]=black;
}
Output( chessStates);
if( count%2==choice )
{
if( count!=1)
{
cout<<"你 下: ";
output(tempB);
cout<<endl;
}
cout<<"电脑下: ";
output(tempA);
cout<<endl;
}
cout<<endl;
count++;
} while( !isGameOver(tempA, chessStates ) && count<=N*N );
cout<<"=============================="<<endl;
if( count>N*N)
cout<<"和棋"<<endl;
else
{
if( chessStates[tempA.x][tempA.y]==white)
cout<<"恭喜你,你赢了!"<<endl;
else cout<<"好可惜啊,你输了!"<<endl;
}
cout<<"=============================="<<endl;
} while( isPlayAgain());
return 0;
}
//////////////////////////////////////////
void Output( States A[][N] )
{
cout<<" ";
for( int a=0; a<N; a++ )
{
if( a<9 )
cout<<' '<<char( int('1')+a );
else
cout<<' '<<char( int('A')+a-9);
}
cout<<endl;
for( int i=0; i<N; i++ )
{
if( i<9 )
cout<<char( int('1')+i);
else
cout<<char( int('A')+i-9);
cout<<" "<<'|';
for( int j=0; j<N; j++ )
{
switch( A[i][j] )
{
case blank: cout<<' '<<'|';
break;
case black: cout<<'O'<<'|';
break;
case white: cout<<'#'<<'|';
break;
}
}
cout<<endl;
}
}
//------------------------------
void initiChessMarks( int A[][N] )
{
for( int i=0; i<N; i++ )
for( int j=0; j<N; j++)
{
int sum=0;
for( int count=1; count<=4; count++)
{
if( i-count>=0 )
sum++;
if( j-count>=0 )
sum++;
if( i+count<N )
sum++;
if( j+count<N )
sum++;
if( i-count>=0 && j-count>=0 )
sum++;
if( i+count<N && j+count<N )
sum++;
if( i-count>=0 && j+count<N )
sum++;
if( i+count<N && j-count>=0 )
sum++;
}
A[i][j]=sum;
}
}//这样,越靠边的原始分会越低
//---------------------------
unsigned int random()
{
int multiplier=2743;
int adder=5923;
randSeed=multiplier*randSeed+adder;
return randSeed;
}//线性同余
//--------------------------------
void updateMarks( int chessMarks[][N], const States chessStates[][N] )
{
States beginStates;
States lastStates;
States temp;
int count;
int sum;
for( int i=0; i<N; i++)
for( int j=0; j<N; j++)
if( chessStates[i][j]!=blank)
chessMarks[i][j]=0;
else
{
sum=0;
for( count=1; count<=4; count++ )
{
if( i-count>=0 )
sum++;
if( j-count>=0 )
sum++;
if( i+count<N )
sum++;
if( j+count<N )
sum++;
if( i-count>=0 && j-count>=0 )
sum++;
if( i+count<N && j+count<N )
sum++;
if( i-count>=0 && j+count<N )
sum++;
if( i+count<N && j-count>=0 )
sum++;
}//endfor
for( int z=1; z<=2; z++)
for( int k=1; k<=8; k++ )
{
int x=i;
int y=j;
int n=0;
int mark;
if( k%2==1)
temp=blank;
for( count=1; count<=N; count++)
{
if( z==1 )
{
switch( k )
{
case 1: x--; break;
case 2: x++; break;
case 3: y--; break;
case 4: y++; break;
case 5: x--; y--; break;
case 6: x++; y++; break;
case 7: x++; y--; break;
case 8: x--; y++; break;
}
}
else
{
switch( k )
{
case 1: x++; break;
case 2: x--; break;
case 3: y++; break;
case 4: y--; break;
case 5: x++; y++; break;
case 6: x--; y--; break;
case 7: x--; y++; break;
case 8: x++; y-- ; break;
}
}
if( !(x>=0 && x<N && y>=0 && y<N) )
break;
else
{
if( count==1)
{
beginStates=lastStates=chessStates[x][y];
if( temp==beginStates && temp!=blank)
n++;
if( temp!=beginStates && temp!=blank && beginStates!=blank)
n--;
temp=beginStates;
if( beginStates==blank )
mark=5;
else mark=8;
}
if( chessStates[x][y]!=lastStates && lastStates!=blank )
{
if( chessStates[x][y]!=blank )
n--;
break;
}
if( chessStates[x][y]!=blank )
{
n++;
lastStates=chessStates[x][y];
}
if( n==4 )
break;
}//endif
}//endfor
if( !(x>=0 && x<N && y>=0 && y<N) && n>0)
n--;
sum=sum+(mark*pow(3,n)-mark)/2+3*n;
}//endfor
chessMarks[i][j]=sum;
}
}///////////////////////////////////////////////
/* 这是最要的函数,是用来更新棋盘分数的。我开始的时候的是下面那样子的,
后来我让它们两个函数互相下,比较过了,上面的那个比较厉害一点点,为什么,我也不知道。我就知道,上面的那个函数是算了两次分,下面的只算一次。但是不是下面的算法算两次。我也说不清楚了。
//////////////////////////////////////////////////
void updateMarks( int chessMarks[][N], const States chessStates[][N] )
{
States beginStates;
States lastStates;
States temp;
int count;
int sum;
for( int i=0; i<N; i++)
for( int j=0; j<N; j++)
if( chessStates[i][j]!=blank)
chessMarks[i][j]=-1;
else
{
sum=0;
for( count=1; count<=4; count++ )
{
if( i-count>=0 )
sum++;
if( j-count>=0 )
sum++;
if( i+count<N )
sum++;
if( j+count<N )
sum++;
if( i-count>=0 && j-count>=0 )
sum++;
if( i+count<N && j+count<N )
sum++;
if( i-count>=0 && j+count<N )
sum++;
if( i+count<N && j-count>=0 )
sum++;
}//endfor
for( int k=1; k<=8; k++ )
{
int x=0;
int y=0;
int n=0;
int mark;
for( count=1; count<=N; count++)
{
switch( k )
{
case 1: x++; break;
case 2: y++; break;
case 3: x--; break;
case 4: y--; break;
case 5: x++; y++; break;
case 6: x--; y++; break;
case 7: x--; y--; break;
case 8: x++; y-- ; break;
}
if( !( i+x>=0 && i+x<N && j+y>=0 && j+y<N) )
break;
else
{
if( count==1)
{
beginStates=lastStates=chessStates[i+x][j+y];
if( i-x>=0 && i-x<N && j-y>=0 && j-y<N )
{
temp=chessStates[i-x][j-y];
if( temp==beginStates && temp!=blank )
n++;
if( temp!=beginStates && temp!=blank && beginStates!=blank )
n--;
}
if( beginStates==blank )
mark=5;
else mark=8;
}
if( chessStates[i+x][j+y]!=lastStates && lastStates!=blank )
{
if( chessStates[i+x][j+y]!=blank )
n--;
break;
}
if( chessStates[i+x][j+y]!=blank )
{
n++;
lastStates=chessStates[i+x][j+y];
}
if( n==4 )
break;
}//endif
}//endfor
if( !( i+x>=0 && i+x<N && j+y>=0 && j+y<N) && n>0)
n--;
sum=sum+(mark*pow(3,n)-mark)/2+n;
}//endfor
chessMarks[i][j]=sum;
}
}*/
//-----------------------------------------
myPoint maxMarkPoint( int chessMarks[][N] )
{
vector< myPoint > max_Marks;
myPoint temp;
int max_mark=chessMarks[0][0];
for( int i=0; i<N; i++)
for( int j=0; j<N; j++)
if( chessMarks[i][j]>max_mark )
max_mark=chessMarks[i][j];
for( i=0; i<N; i++)
for( int j=0; j<N; j++)
if( chessMarks[i][j]==max_mark )
max_Marks.push_back( myPoint(i,j));
temp=max_Marks[random()%max_Marks.size()];
max_Marks.clear();
return temp;
}
//---------------------------------------------
bool isGameOver( myPoint pt, States chessStates[][N] )
{
States temp=chessStates[pt.x][pt.y];
int count;
for( int k=1; k<=8; k++ )
{
int i=pt.x;
int j=pt.y;
if( k%2==1)
count=0;
while( 1 )
{
switch( k )
{
case 1: i++; break;
case 2: i--; break;
case 3: j++; break;
case 4: j--; break;
case 5: i--; j--; break;
case 6: i++; j++; break;
case 7: i++; j--; break;
case 8: i--; j++; break;
}
if( i>=0 && i<N && j>=0 && j<N )
{
if( chessStates[i][j]==temp )
count++;
else break;
}
else break;
if( count==4 )
return true;
}
}
return false;
}
//--------------------------------
myPoint input()
{
char i, j;
int x, y;
cin>>i>>j;
i=toupper(i);
j=toupper(j);
while( !( (i>='1'&&i<='9') || (i>='A'&&i<=char(N-10+int('A'))) ) ||
!( (j>='1'&&j<='9') || (j>='A'&&j<=char(N-10+int('A'))) ) )
{
cout<<"你下的棋步不对"<<endl;
cout<<"请你下棋: ";
cin.ignore( 100, '\n');
cin>>i>>j;
i=toupper(i);
j=toupper(j);
}
cin.ignore( 100, '\n' );
if( i>='1' && i<='9' )
x=int( i-'1' );
else
x=int( i-'A' )+9;
if( j>='1' && j<='9' )
y=int( j-'1' );
else
y=int( j-'A' )+9;
return myPoint(x,y);
}
//------------------------------
void output( myPoint pt )
{
char x;
char y;
if( pt.x<=8 )
x=char( int('1')+pt.x );
else
x=char( int('A')+pt.x-9);
if( pt.y<=8 )
y=char( int('1')+pt.y );
else
y=char( int('A')+pt.y-9);
cout<<'('<<x<<','<<y<<')';
}
//--------------------------------
int Explain()
{
char choice;
cout<<"======================================"<<endl;
cout<<"== 五子棋的小程序 =="<<endl;
cout<<"======================================"<<endl;
cout<<" 先走: 1 后走: 2 "<<endl;
cout<<" 请你选择: ";
cin>>choice;
while( !(choice=='1' || choice=='2') )
cin>>choice;
cout<<endl;
cout<<"--------------------------------------"<<endl;
cin.ignore(100,'\n');
return int(choice)-int('1');
}
//---------------------------------
bool isPermit( myPoint pt, States chessStates[][N] )
{
int i=pt.x;
int j=pt.y;
if( i<0 || i>=N || j<0 || j>=N )
return false;
if( chessStates[i][j]!=blank )
return false;
return true;
}
//---------------------------------------
bool isPlayAgain()
{
char input;
cout<<"你想再玩一次吗? (Y/N): ";
cin>>input;
input=toupper( input );
while( !(input=='Y'|| input=='N') )
{
cin>>input;
input=toupper( input );
}
cout<<endl<<endl;
if( input=='Y' )
return true;
else
return false;
}
//////////////////////////////////////////////
不知道你有没有看过了,是不是没有多少注释。说真的,我不知道如何注释。其实只要明白了策略,就可以了。剩下的是如何表达的问题。如何表达,不同的人有不同的风格的。
////////////////////////////////////////////
其实还可以改进的,就好象悔棋和覆盘的功能,上面是没有的,也好容易写出来。只要将每一次下棋的坐标用点表示,放进一个向量,悔棋的时候,将最后下的坐标上的棋盘状态变为blank, 再将最后的弹出,可以让你悔任意步。当然,也可以用栈来存储了。不过用栈不可以访问小标,不可以覆盘。用向量,可以从0开始,下标加1,来访问每一步棋。如果你想,也可以用栈来悔棋,用队列来覆盘。
////////////////////////////////////////////
上面的其他函数,好象什么输入棋盘啊!输入坐标啊!都不难,真的要弄个界面也不难。还是那句,那个下棋的策略最重要。我那个策略,只是考虑了一步棋的情况,所以电脑也没有那么聪明。我试过在qq游戏上让它和别人下,想程序输入别人的棋步,再用程序的棋步来回应别人。下了两盘,赢了一盘。另一盘,电脑犯了个好低级的错误。当然了,它的对方也好笨,我下五子棋也好笨,也教不了电脑多大本事了。有些人下五子棋老是下成一个洞一个洞的,我自己都不会,更改不了程序了。不过是自己编来玩玩而已。你想想,自己编一个小程序,跟着自己和程序下,也挺有趣的。
///////////////////////////////////////////
是这样啦。祝大家狗年愉快