回 帖 发 新 帖 刷新版面

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

自己写的一个五子棋的小程序。挺简单的一个算法,欢迎大家指教。
电脑下棋时是根据棋形来计算棋盘上每一个格子的分数,就下在分数最高的一个方格上面。棋盘有三种状态,有黑棋(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个回复)

沙发

强,目前看不懂
老大你学C多长时间了啊

板凳

作为学习之余的练习做的挺不错的!

3 楼

程序很规范,看起来就比较喜欢.注释写得也详细.Perfect performance!

4 楼

不要说老大啦。你比我大多了。
恩,大概9个月左右吧。当当兴趣啦。
是了,我学的是C++,不是C来的,C和C++是不一样的,是不同的语言。有些人说会了C++,就会了C。其实两种语言的主要区别不是在语法上面,是思想上面的。
还有,下次呢。应该问别人,你学编程多久啦?不要问你学什么什么语言多久了?语言本身是一中表达自己思想的工具,不是最重要的。
语言本身不重要,语言所有的语法也不重要,用语言根据一定的语法来表达的思想才是最重要的。所以好多本身不是学计算机的对计算机的发展也有好大的贡献,因为好多思想都是相通的。
我自己觉得好多人,学一种语言,比喻C++吧,就好象学会了语法就是会了。一天就抱着本关于语法的书来看,是写不出一个象样的程序的。可以说,学不好一门语言,不是因为你没有努力去看关于这门语言的书,是因为你整天去看关于这门语言的书(指语法方面),而忽略了其他方面的,好象数据结构啊!算法之类的,比C++语言、语法本身更为重要。还有数学和逻辑也好重要。
现在我觉得编程真的是好有趣的,也可以说是一门艺术啦。好羡慕计算机系的同学啦,我自己就没有这样多的时间,在这上面花的时间多了,自己本身的课程就没有这样多的时间了。



5 楼

楼主,我应该算比较仔细地看了你的程序,有些地方看得不是很明白:
对 空空空空空这种情况,第一个空格它的分数怎么算?这几个空的分数是不是一样的?你只讨论黑白棋连着的情况,

6 楼


[em1]
我好佩服你,toyasimple。
  我是刚学过C语言的新手,很喜欢编程。看了你的程序,发现include后面的头文件名都没有加“.h”,我还以为会出错,结果运行得很好,这是什么原因? 

(刚刚犯了个错误,第一次发此帖时错发成新帖了)

7 楼

我也是刚刚开始学C语言的  
好多地方没明白怎么回事  哪位指点下  
 请问下 你写这个程序 花了多久时间 ?

8 楼

真是你自己写的?入我群吧,

9 楼

听君一席话,真的让我惭愧之至阿!我是计算机软件工程专业的,照理说应很了解编程才对,可学的一塌糊涂还自以为是认为别人也跟我一样糊涂,真的羞愧难当,在此希望能叫你一声大哥,希望在以后的人生道路上能有你的扶持!谢!                                                        弟方平

10 楼

“using namespace std;”用了这条语句就不用加.h了
我还是挺佩服楼主的!!

我来回复

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