回 帖 发 新 帖 刷新版面

主题:[原创]自己写的一个五子棋小程序

自己写的一个五子棋的小程序。挺简单的一个算法,欢迎大家指教。
电脑下棋时是根据棋形来计算棋盘上每一个格子的分数,就下在分数最高的一个方格上面。棋盘有三种状态,有黑棋(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游戏上让它和别人下,想程序输入别人的棋步,再用程序的棋步来回应别人。下了两盘,赢了一盘。另一盘,电脑犯了个好低级的错误。当然了,它的对方也好笨,我下五子棋也好笨,也教不了电脑多大本事了。有些人下五子棋老是下成一个洞一个洞的,我自己都不会,更改不了程序了。不过是自己编来玩玩而已。你想想,自己编一个小程序,跟着自己和程序下,也挺有趣的。
///////////////////////////////////////////
是这样啦。祝大家狗年愉快

回复列表 (共18个回复)

11 楼

看看那个循环 for( count=1; count<=N; count++)
其实是向整个棋盘扫描的,因为棋盘是N*N的,无论空格是在棋盘上的哪一个地方,无论是向哪个方向扫描,要扫描的格子数都不会超过N。
看看一种极端的情况 空空空空空空空空(连续很多个空,到了边界),所有空格在这个方向上,没有棋子,都算原始分。
看看另一种情况,空空空空空空空空空黑黑黑,第一个空格是从5分起算,N=3,的等比数列求和。第二个空格也是,第三个空格也是,第四……也是。到了黑棋附近的空格,就是从8分开始起算,N=3的等比数列求和。可能有人说,第一个空格明显没有第二个空格有利嘛,为什么会算一样的分啊?注意,我们是要电脑下在最有利的地方,只要知道最有利是在什么地方,就可以了,不必从小到大,将各个空格的相对有利情况都要知道。就像我们选班上一个成绩最好的人去竞赛,只要知道谁最厉害就行了,不必将班上左右人的成绩都排列出来。所以,既然都认为附近的空格最要利,我们就将它的分数定得最高。就不比理会不是附近的空格的分数大小了。
注意,上面是说一个方向的,其实是向八个方向扫描的,一个方向上的分数相同,并不代表空格的分数相同。每一个方向算了两次。所以有
for( int z=1; z<=2; z++)
for( int k=1; k<=8; k++ )
至于为什么,要每个方向算两次呢,其实我开始的时候是只算一次的,就是下面的算法。但是比较之后发现上面的算法,赢的机会会高一些。
我要电脑下棋的策略很简单,只是看一步棋,总是下在原来有棋的地方,没有想到下面几步棋,所以电脑也比较笨。不过也可以勉强可以下下吧。
//////////////////////////
至于写了多久的问题,好多时候是想的时候比真正编码的时候要多得多的。这个小程序从构思到表达出来,用了我三天时间。是突然想到要写的。其他的都很好办,就是策略改了很多次。开始是用等差数列的,后来才改成等比。开始没有考虑边界问题的,回来也要到边界时候N--了。开始没有考虑另一方向有自己或别人的棋子挡着的,后来有自己的棋子就N++,有别人的棋子就N--。开始也不知道五子棋是15*15的,后来也改成了可以改动棋盘大小了。开始是用数字输入坐标的,好象 13 15 回车这样,后来也改成了用数字结合英文字符来输入。
/////////////////////////////
本来是想在写一个覆盘和悔棋的功能的,不过要开学了,没有心情了。也有点困了。就当作留给大家的题目吧。
/////////////////////////
我也不是很厉害,不要佩服我,我只是广大编程爱好者的一员,还处在低级的低级阶段。至少现在还不会做界面。不然输入输出可以更好看一点。还有,本来应该是要写一个chess类的,不过我本人对类还不是很熟悉,还没有真正的理解通,要再看看书,所以就用函数,要C的风格来写了。
///////////////////////////
至于为什么不带.h也能够编译呢,其实是C++语言的一种新的标准。下面
using namespace std;
是用到命名空间,以前是没有命名空间的。那什么是命名空间呢,很难三言两语说清楚,自己看看书吧。
(上面是尽我的能力来解释了,我的文字表达可能不太好,请见谅)

12 楼

学一下

13 楼

--------------------Configuration: Games - Win32 Debug--------------------
Compiling...
wuziqi.cpp
E:\Microsoft vitual c++ spark6\MSDev98\MyProjects\c、c++源文件\Games\wuziqi.cpp(285) : error C2084: function 'void __cdecl updateMarks(int [][15],const enum States [][15])' already has a body
执行 cl.exe 时出错.

Games.exe - 1 error(s), 0 warning(s)

怎么有错误呢?

说什么:"function 'void __cdecl updateMarks(int [][15],const enum States [][15])' already has a body",这是什么意思呀/?

我是在 VC++6.0下编译!

14 楼

看来10楼还不知道 using namespace std 是干什么用的……

15 楼

厉害厉害

16 楼

我都不是程序的对手 下3次都输了 厉害啊
我学C++一年多了  可我根本看不懂这算法 好复杂啊 没总情况都分别处理的?

17 楼

为什么我无法通过编译?就你这程序,能把*.c给发出来吗?

18 楼

其实这个是VC2005和VC6.0要使用的使用空间(namespace),而TC3.0则不要使用空间,只要在后面加上.h就可以了

我来回复

您尚未登录,请登录后再回复。点此登录或注册