五子棋程序设计(C语言、人机对战、禁手)

五子棋程序设计(C语言、人机对战、禁手)
一、程序需求分析
1.1五子棋简介
五子棋是全国智力运动会竞技项目之一,是一种两人对弈的纯策略型棋类游戏。
五子棋有两种玩法。玩法一:双方分别使用黑白两色的棋子,下在棋盘直线与横线的交叉点上,先形成五子连线者获胜。玩法二:自己形成五子连线就替换对方任意一枚棋子。被替换的棋子可以和对方交换棋子。最后以先出完所有棋子的一方为胜。我们本次程序设计采用的玩法是第一种玩法。
传统五子棋的棋具与围棋相同,棋子分为黑白两色,棋盘为15×15,棋子放置于棋盘线交叉点上。两人对局,各执一色,轮流下一子,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。
1.2程序设计要求
本程序设计要求游戏功能有人与人对弈和人机对弈,并要求黑棋有禁手规则。游戏需要实现功能是游戏双方一方执黑棋,一方执白棋,轮流走棋,每方都试图在游戏结束前让自己的棋子五子相连,首先实现五子相连的一方获胜。程序执行过程中,要求棋盘、棋子时时可见,并且人可以通过按键盘按键摆放棋子。
1.3程序需求分析
根据功能需求,将程序分为棋盘显示、玩家控制、胜负判断、人机对战和机器人落子计分五个模块,以下分析各模块的需求。
棋盘显示模块:游戏开始后要求生成15×15的棋盘图像,游戏下方显示0、退出,1、电脑VS玩家,2、玩家VS玩家三种选择,当用户选择后,进去下棋界面,要求实时显示棋盘上已落下的棋子;分出胜负后,要求给出游戏结束画面。
玩家控制模块:程序开始时,需玩家确定而后开始游戏;游戏过程中,两个玩家通过键盘,选择落子。
胜负判断模块:实时监测棋盘上棋子,一旦某一色棋子出现五子连线,终止游戏程序,并着色连成一线的五子,弹出该色玩家胜出界面。
人机对战模块:程序设计需要拥有人机对战模块。
机器人落子计分模块:这个模块是通过计算机器人下每一步棋子的得分情况,来让人机对战是机器人更加聪明。
1.4程序整体设计流程图

二、玩家与玩家对战设计思路及主要程序
2.1设计思路
现在我想先说一说玩家对战部分的实现思路,因为这一部分的实现会简单很多。而人机对战规则和代码比较复杂留在下一点分析,因为人机对战这一部分涉及策略的问题,这包含几个层次,比如让电脑找空位随机落子,更进一步可以在对方活三或其他情况的棋子附近随机落子;更高级的策略可以让计算机考虑到更多的情况和步骤,但是程序也会复杂很多。
第一步,显示棋盘。绘制出来的棋盘如下:

第二步,执行落子。这个过程我们使用了playermove(int **state, int row, int column, int order)这个函数让玩家执行落子。当玩家输入要下的位置的坐标的时候,棋子坐标就会被写入state[x][y]数组内,然后重新显示棋盘,棋盘上就会出现棋子。如果输入的坐标上已经有棋子了,会提示“该位置已经有棋子了,请下别的位置”。落子过程如下:

当完成上面两个步骤之后剩下的就是判断黑棋是否有禁手和判断双方胜负的函数的编写了,这两个函数我会在第四段单独讲解。
2.2设计流程图

2.3主要程序代码
int main()
{
while(1)
{
order = 1;
int i,j;
//int *board;
//1黑方,2白方;规则是黑方先走,但黑方会有禁手
board = malloc(sizeof(int
)*ROW);
for(i = 0; i < ROW; i++)
board[i] = malloc(sizeof(int)*COLUMN);
for(i = 0; i < ROW; i++)
for(j = 0; j < COLUMN; j++)
board[i][j] = 0;
draw(board, ROW, COLUMN);
int P=ChoiceMode();
if(P2) //玩家VS玩家
while(1)
{
playermove(board, ROW, COLUMN, order);
draw(board, ROW, COLUMN);
if(z
1)
{
if(order1)
printf(“黑方胜利!”);
else
printf(“白方胜利!”);
z=0;
break;
}
else if(z
-1)
{
printf(“和棋!”);
z=0;
break;
}
else if(z==-2)
{
printf(“黑方禁手犯规!白方胜利!”);
}
else
;

            order = order%2+1;
        }
    }
    else
        break;
    system("pause");
    for(i = 0; i < ROW; i++)
        free(board[i]);
    free(board);
}

return 0;

}

三、人机对战设计思路及主要程序
3.1设计思路
首先在main函数里选择玩家VS电脑,调用不同的函数。轮到电脑下子时,基本思路就是遍历棋盘上的每一个空位,并逐个计算价值量,寻找价值量最大的那个位置,并将这个位置传回score函数中进行价值量的比较,并且最终进行电脑的下子。
每找到一个空档,首先逐一检查它上、下、左、右、左上、左下、右上、右下八个方位是否有棋子。例如该空档上方向无子则跳过价值量为零,检查到左下发现有棋子,则继续查看有几个棋子,从是否有一个颜色相同的棋子开始,一直到有四个棋子,逐一累加价值量,每次应判断这些棋子的颜色是否和电脑自己的颜色相同,有相同、不同两种情况,两者所叠加的价值量不同,然后再判断这几个颜色相同的棋子组成的这条线的下一个位置是否有棋子,有颜色相同、不同、无棋子三种情况,三者所叠加的价值量不同。
另外为了使价值量的区别更大,更容易把控,判断出不同数量的连续棋子后会先加不同的权重,数量越多,权重指数级增长。另外,为了区分活三和连四两种特殊情况,为它们单独加了极大的价值量,方便电脑判断。

3.2设计流程图

3.3主要程序代码
int main()
{
while(1)
{
order = 1;
int i,j;
//int *board;
//1黑方,2白方;规则是黑方先走,但黑方会有禁手
board = malloc(sizeof(int
)*ROW);
for(i = 0; i < ROW; i++)
board[i] = malloc(sizeof(int)*COLUMN);
for(i = 0; i < ROW; i++)
for(j = 0; j < COLUMN; j++)
board[i][j] = 0;
draw(board, ROW, COLUMN);
int P=ChoiceMode();
if(P1) //人机
{
While(1)
{ playermove(board, ROW, COLUMN, order);
draw(board, ROW, COLUMN);
if(z
1)
{
if(order1)
printf(“黑方胜利!”);
else
printf(“白方胜利!”);
z=0;
break;
}
else if(z
-1)
{
printf(“和棋!”);
z=0;
break;
}
else if(z==-2)
{
printf(“黑方禁手犯规!白方胜利!”);
}
else
;
order = order%2+1;

            actionByAI(board,ROW,COLUMN);
            draw(board, ROW, COLUMN);
            if(z==1)
            {
                if(order==1)
                    printf("黑方胜利!");
                else
                    printf("白方胜利!");
                z=0;
                break;
            }
            else if(z==-1)
            {
                printf("和棋!");
                z=0;
                break;
            }
            else if(z==-2)
            {
                printf("黑方禁手犯规!白方胜利!");
            }
            else
                ;
            order = order%2+1;
          }
    }

四、关键函数的讲解
2.1棋盘显示
我们要先绘制出一个1515的棋盘。为此,用一个1515的二维数组来储存棋盘上每一个位置的信息(应包括此处为空或者有白子或黑子),把这个数组命名为state,其中每一个元素表示为state[ i ][ j ]。棋盘是完全由制表符组成的。因此,我们需要将数组board存储的数值与制表符进行一个对应。弄清了棋盘每一个位置的信息的储存方式后,我们就需要一个函数,读取实时的棋盘信息,并根据读取到的信息绘制棋盘,并且使得棋盘的每一个位置能直观地读取到坐标,我们把这个函数命名为void draw(int **state, int row, int column),棋盘显示如下:

函数具体内容如下:
void draw(int **state, int row, int column)
{
system(“cls”); //ÇåÆÁ
int i,j;
printf(" “);
for (i = 0; i < column; i++)
printf(”%c “, (char)(i+65));
printf(”\n");
for (i = 0; i < row; i++)
{
printf("%c “, (char)(i+65));
for (j = 0; j < column; j++)
{
switch(state[i][j])
{
case 0:
if (j > 0 && j < column-1)
printf(”%s", (i == 0 ? TOP_CENTER : i == row-1? BOTTOM_CENTER : INTERNAL));
else if (j == 0)
printf("%s", (i == 0 ? LEFT_TOP : i == row-1 ? LEFT_BOTTOM : LEFT_CENTER));
else if(j == column-1)
printf("%s", (i == 0 ? RIGHT_TOP : i == row-1 ? RIGHT_BOTTOM : RIGHT_CENTER));
break;
case 1:
printf("%s", BLACK);
break;
case 2:
printf("%s", WHITE);
break;
default:
break;
}
}
printf("\n");
}
}

2.2判断胜负
这是整个程序中需要考虑的情况最多的一个部分。我们既需要考虑横向棋子的布局,还需要考虑纵向,更复杂的是还需要考虑斜率分别为-1和1的直线上的落子情况。
横向和纵向的判断容易理解,斜向的会复杂一些,尤其是在靠近四个角落的地方,因为我们需要保证有足够的空间使得能够有五颗棋子连成一条线,因此在考虑斜向时我又将每种斜率的直线分为了两种情况。
主要代码见附录1

2.3黑棋禁手
黑棋禁手规则:五子棋术语,指对局中禁止先行一方(黑方)使用的战术,具体包括黑方一子落下时同时形成双活三、双四或长连等三种棋形。禁手只对黑方有效,白方无禁手。黑方禁手的位置称为禁手点。
禁手的分类有三种,分别是三三禁手、四四禁手和长连禁手。三三禁手的意思是黑方一子落下同时形成两个或两个以上的活三(或嵌四),此步为三三禁手。 注意:这里一定要两个都是 “活”三才能算。“四三三”指黑方一步使一个四、两个活三同时形成,事实上是一种特殊的三三禁手。四四禁手的意思

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值