1.创建项目、源文件、头文件
首先,在编译器中创建一个新项目,然后,添加源文件和头文件。
源文件一般创建两个:test.c和game.c,test.c用于测试,在编写程序过程中,可以不断对当前代码块进行测试,避免程序全部完成之后再测试,编译器报出一堆错误,难以对错误进行定位。
头文件创建一个:game.h,将函数的声明和头文件等放在game.h中,这样对于在test.c和game.c中都需要引的头文件,只需要在game.h中引一次就可以了。
2.代码实现三子棋小游戏
首先创建三个文件
game.h //后面在代码编写过程中,不断丰富该文件内容,存放函数实现过程中所需的头文件、函数声明等
#include <stdio.h>
void game();//声明game函数(不要忘了函数声明后面有;)
test.c//每编写一个功能模块可进行一次测试,确定当前代码没有问题时再继续写
#include "game.h"
void menu()//打印菜单
{
printf("*************************\n");
printf("*****0.exit***1.play*****\n");
printf("*************************\n");
}
int main()
{
int input=0;
srand((unsigned int)time(NULL));//随机数的生成起点,用于后面电脑下棋时位置的随机生成
do
{
menu();
printf("请选择\n");
scanf("%d",&input);
switch(input)
{
case 0:
printf("退出\n");
break;
case 1:
game();
break;
default:
printf("选择错误,请重新选择\n");
break;
}
}while(input);
return 0;
}
在有了上面两个模块之后,剩下的就是实现游戏的具体过程了,放在game.c中
这里简单分析一下实现游戏过程的几个问题(更加具体、细节的问题见代码备注):
(1)棋盘上的坐标用一个 二维数组存起来,本文所创建的二维数组比实际棋盘大,行、列分别比实际棋盘多2。以棋盘大小3*3为例
为什么这样做呢?
因为后面每走一步都要判断玩家/电脑是否赢了,因此要判断当前棋盘中是否有满足行/列/对角线三个连续的位置同时为玩家/电脑所下棋子的情况,但对于棋盘边缘一圈位置的判断会超出棋盘,如果对于边缘位置都单独判断会比较麻烦,因此,实际创建的数组大小比棋盘的实际大小要大,且多出的部分不会被下棋。
(2)本文打印的棋盘格式(以3*3的棋盘为例)
game.c
#include "game.h"
void game()
{
char ret='C';
char Board[ROWS][COLS]={0};
InitBoard(Board,ROWS,COLS);//用于初始化棋盘
DisplayBoard(Board,ROW,COL);//打印棋盘
while(ret=='C')
{
printf("玩家走\n");
PlayerMove(Board,ROW,COL);//玩家放置棋子
DisplayBoard(Board,ROW,COL);
if((ret=Iswin(Board,ROWS,COLS))!='C')//判断玩家是否赢了,没赢游戏继续,电脑走
break;
else
{
printf("电脑走\n");
ComputerMove(Board,ROW,COL);
DisplayBoard(Board,ROW,COL);
ret=Iswin(Board,ROWS,COLS);
}
}
if(ret=='#')
printf("电脑赢\n");
else if(ret=='*')
printf("玩家赢\n");
else
printf("平局\n");
}
void InitBoard(char(*Board)[COLS],int rows,int cols)
{
int i,j=0;
for(i=0;i<rows;i++)
{
for(j=0;j<cols;j++)
{
Board[i][j]=' ';//用' '初始化棋盘,初始的棋盘大小是ROWS*COLS
}
}
}
void DisplayBoard(char(*Board)[COLS],int row,int col)//打印棋盘只需要打印大小为:ROW*COL
{
int i,j=0;
for(i=1;i<=row;i++)
{
for(j=1;j<=col;j++)
{
printf(" %c ",Board[i][j]);
if(j!=col)
printf("|");
else
printf("\n");
}
if(i!=row)
{
for(j=1;j<=col;j++)
{
printf("---");
if(j!=col)
printf("|");
else
printf("\n");
}
}
}
}
void PlayerMove(char(*Board)[COLS],int row,int col)
{
int x,y=0;
while(1)//只有当输入的坐标在合法范围(ROW*COL里),并且没有被占用,棋子才成功放置
{
printf("请输入坐标:\n");
scanf("%d%d",&x,&y);
if(x<1||x>row||y<1||y>row)
{
printf("坐标非法,重新输入\n");
}
else if(Board[x][y]!=' ')
{
printf("坐标被占用,重新输入\n");
}
else
{
Board[x][y]='*';//用‘*’代表用户所下棋子
break;
}
}
}
void ComputerMove(char(*Board)[COLS],int row,int col)
{
int x,y=0;
while(1)
{
x=rand()%row+1;
y=rand()%col+1;//随机产生x:1-ROW,y:1-COL
if(Board[x][y]==' ')
{
Board[x][y]='#';//用‘#’代表电脑所下棋子
break;
}
}
}
char Iswin(char(*Board)[COLS],int rows,int cols)
{
int i,j=0;
for(i=1;i<rows-1;i++)
{
for(j=1;j<cols-1;j++)
{
if((Board[i][j]!=' '))
{
if(((Board[i][j]==Board[i][j-1])&&(Board[i][j]==Board[i][j+1]))||
((Board[i-1][j]==Board[i][j])&&(Board[i][j]==Board[i+1][j]))||
((Board[i-1][j-1]==Board[i][j])&&(Board[i][j]==Board[i+1][j+1]))||
((Board[i+1][j-1]==Board[i][j])&&(Board[i][j]==Board[i-1][j+1])))
return Board[i][j]; //当电脑/用户赢了时,返回Board[i][j]('#'或'*')
}
}
}
for(i=1;i<rows-1;i++)
{
for(j=1;j<cols-1;j++)
{
if(Board[i][j]==' ')
return 'C';//电脑/用户都没有赢,并且棋盘有空格时,游戏继续,返回‘C’
}
}
return 'E';//电脑/用户都没有赢,但是棋盘没有空格了,平局,返回'E'
}
根据在编写game.c过程中,game.h已完善至如下代码所示:
game.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#define ROW 3
#define COL 3
#define ROWS ROW+2
#define COLS COL+2
void game();
void InitBoard(char(*Board)[COLS],int rows,int cols);
void DisplayBoard(char(*Board)[COLS],int row,int col);
void PlayerMove(char(*Board)[COLS],int row,int col);
void ComputerMove(char(*Board)[COLS],int row,int col);
char Iswin(char(*Board)[COLS],int rows,int cols);