1.规则了解:
2.游戏程序预分析
3.设计游戏
1规则了解:
想必大家都知道,三子棋是互相各下一子,谁先在对角线,平行线方向连成三个连续相同的子谁就赢得胜利.
2游戏程序分析:
结合规则可知,我们要实现三子棋,首先得有个菜单,让玩家选择是否玩,随后就进入了我们的游戏程序模块,在这个模块中,第一步我认为是得先有个棋盘,这个棋盘可以显示我们的下棋情况,这时候才到我们真正下棋,大致代码思路理清了,接下来我们就开始设计游戏,并逐渐补充代码模块。提炼如下:
(1)打印菜单
(2)打印棋盘
(3)下棋
3:设计游戏
第一步写出主函数,这个主函数放在源.c文件,我们需要一个变量a,用来接收用户选择是否玩游戏,1是玩,0是退出,为了简便,我们用switch语句来写出1和0的分支,并用default字句应对用户输入非1非零的情况,我们希望这个游戏可以反复的玩,也就是我们可以多次选择是否玩游戏,并且至少要用户选择一次,很明显用do while,这个循环是否结束就恰好可以用变量a的值,此时可见a的定义放在循环外,放在内部定义a不可以作为循环判断条件。
void menu()
{
printf("*******1.play *************\n");
printf("*******0.exit *************\n");
}
int main()
{
int a = 0;
do
{
//打印菜单
menu();
//接收选择
scanf("%d", &a);
switch (a)
{
case 1:
game();
break;
case 0:
break;
default:
printf("请重新输入\n");
}
} while (a);
return 0;
}
第二步:
用户选择一,我们进入玩游戏的程序game()函数,(我们在设计时将游戏功能实现函数如判断输赢函数,玩家电脑下棋函数放在game.c文件,这些函数的申明放在game.h这个头文件)这时我们打印出棋盘,让用户下棋,我们得想想这个棋盘对应C语言的什么,是什么能储存我们在下子时的那个记号,比如*和#,并且是我们输入坐标它就能存储到相应位置并且打印显示,那这不就是二维数组嘛,二维数组虽然是连续存放的,但我们也可以理解为是一个正方形表格,我们打印这个表格元素的时候加上框框不就是我们的棋盘嘛。上代码!先用自定义函数initial把棋盘扫干净。再用自定义函数Printf打印棋盘。
void initial(char arr[Row][Line], int row, int line)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < line; j++)
{
arr[i][j] =' ';
}
}
}
void Printf(char arr[Row][Line], int row, int line)
{
int cou = 0;
int i = 0;
for (i=0;i<row;i++)
{
int j = 0;
for (j = 0; j < line; j++)
{
printf(" %c ",arr[i][j]);
if(j<line-1)
printf("|");
}
printf("\n");
if (i < row - 1)
{
for (j = 0; j < line; j++)
{
printf("___");
if (j < line - 1)
printf("|");
}
}
printf("\n");
}
}
这些函数都涉及到了对二维数组的传参,奈何本人知识有限,我只能说二维数组传参格式之一就是这样,行列大小也要上传,Row,Line是我在game.h这个头文件用宏定义的标识符常量,便于我们以后将三子棋优化为n子棋时对代码的改写。我们把数组元素和边框' | '作为作为一组打印,循环一次打印一组,此外,打印’ | '是if控制的,所以打印第三组的时候‘ | ’未打印出来,构造成一个半开半闭的格子,内层循环结束第一层打印完毕,而这样的格子数目等于数组列数,所以内层循环结束的标志,就是小于列数line,因为0到line的数字个数等于line,这个想必大家知道吧。
然后我们再用下面这个内层循环打印‘—— |‘,结束与上一个内层循环一样,只是打印元素有差别,我们注意到这个循环是用if(i<row-1)控制的,这使得'—— |'这种的只有两层,
if (i < row - 1)
{
for (j = 0; j < line; j++)
{
printf("___");
if (j < line - 1)
printf("|");
}
}
printf("\n");
此时我们终于到了下棋,首先是我们下棋,调用player_move函数,我们先输入坐标,这个坐标和计算机看到的是不一样的,比如在棋盘我们认为是第一行第一列,而在数组中它则[0][0]号元素,所以我们要将坐标减1,才能转换为在数组的位置 。
我们输入了一个坐标还需要不停地判断这个坐标是否被占用,以及是否在数组内,直到玩家输入的坐标是合法的,我们此时才能跳出循环,一次下棋结束。
void player_move(char arr[Row][Line], int row, int line)//玩家下棋
{
int x = 0;
int y = 0;
while (1)
{
printf("玩家请输入坐标\n");
scanf_s("%d %d", &x, &y);
if (x <= row && y <= line && x >= 1 && y >= 1)
{
if (arr[x - 1][y - 1] == ' ')
{
printf("坐标合法,下棋成功\n");
arr[x - 1][y - 1] = '*';//玩家下棋标志为*
break;
}
else
printf("坐标被占用,请重新输入\n");
}
else
printf("坐标非法请重新输入\n");
}
}
接下来就是电脑下棋,此时我们只需要让电脑随机在一个空的棋盘格子处下棋,这就涉及到了两个步骤,第一:产生一个随机数,第二找到一个空位置。所以我们还是用一个死循环,而获取一个随机数则用rand()函数,这个函数的头文件为<stdlib.h>,还要设置一次随机数生成起点,就是在主函数中调用一次srand((unsigned int )time(NULL)),将随机数用%取模,我们得到的数就可以直接转换为坐标,之后就是用If判断该位置是否是空的,是空的就把此处的赋成电脑下棋标志‘#’。
void computer_move(char arr[Row][Line], int row, int line)
{
while (1)
{
int x = rand() % row;
int y = rand() %line;
if (arr[x ][y ] == ' ')
{
arr[x ][y ] = '#';//电脑下棋标志为#
break;
}
}
}
此时我们已经把双方下棋的代码编写好了,那是不是还要有能判断胜负的函数,在3乘3的棋盘上我们每次下棋之后只会出现玩家赢返回‘*’,电脑赢返回‘#’,平局返回‘Q’(棋盘满了同样也要结束游戏,不然我上面那么多个死循环就会因为找不到空位而无法结束),以及游戏继续返回'C'这四种情况,赢得标志是行相等,列相等,或者两条斜线相等
行相等:
int i = 0;//判断行相等
for (i = 0; i < row; i++)
{
if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2]&&arr[i][0]!=' ')
return arr[i][0];
}
列相等:
int j = 0;//判断列相等
for (j = 0; j < line; j++)
{
if (arr[0][j] == arr[1][j] && arr[1][j] == arr[2][j] && arr[0][j] != ' ')
return arr[0][j];
}
斜线相等:
//判断斜线相等
if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2]&&arr[0][0]!=' ')
return arr[0][0];
if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0]&&arr[0][2]!=' ')
return arr[0][2];
在此我们可以看到我们返回的是数组中出现行相等,列相等的元素,而没有这是为了这个函数的可移性变好,不然我们得给玩家和电脑各自编写一个判断胜负函数,显得十分啰嗦,还不如直接返回满足胜条件的元素,再来判断这个返回值是什么来判断谁赢了。
还有判断棋盘是否满了:
char is_full(char arr[Row][Line], int row, int line)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < line; j++)
{
if (arr[i][j] == ' ')
return 'C';
}
}
return 'Q';
}
完整代码:
源.c
#include"game.h"
void menu()
{
printf("*******1.play *************\n");
printf("*******0.exit *************\n");
}
void game()
{
//打印棋盘,棋盘存放落子情况
char arr[Row][Line];//三字棋,大小为三行三列
//初始化棋盘
initial(arr,Row,Line);
//打印棋盘
Printf(arr, Row,Line);
char win = 0;
while (1)
{ //玩家下棋
player_move(arr, Row, Line);
//打印玩家下棋结果
Printf(arr, Row, Line);
//判断输赢
win = is_win(arr, Row, Line);//玩家赢,返回*
if ( win!='C')
break;
//电脑下棋
computer_move(arr, Row, Line);
//打印电脑下棋结果
Printf(arr, Row, Line);
win = is_win(arr, Row, Line);//电脑赢,返回# 游戏继续返回C 平局返回Q
if (win!='C')
break;
}
if ('*' == win)
{
printf("玩家赢\n");
}
else if ('#' == win)
{
printf("电脑赢\n");
}
else if ('Q' == win)
{
printf("平局\n");
}
}
int main()
{
int a = 0;
srand((unsigned int )time(NULL));
do
{
//打印菜单
menu();
//接收选择
scanf_s("%d", &a);
switch (a)
{
case 1:
game();
break;
case 0:
break;
default:
printf("请重新输入\n");
}
} while (a);
return 0;
}
game.h
#define Row 3 //“宏定义两个变量的写法”
#define Line 3
#include<time.h>
#include<stdio.h>
#include<stdlib.h>
void initial(char arr[Row][Line], int row, int line);//“注意二维数组的传参”
void Printf(char arr[Row][Line], int row, int line);//打印棋盘
void player_move(char arr[Row][Line], int row, int line);//玩家下棋
void computer_move(char arr[Row][Line], int row, int line);//电脑下棋
char is_win(char aarr[Row][Line], int row, int line);//判断输赢
game.c
#include"game.h"
char is_full(char arr[Row][Line], int row, int line)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < line; j++)
{
if (arr[i][j] == ' ')
return 'C';
}
}
return 'Q';
}
void initial(char arr[Row][Line], int row, int line)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < line; j++)
{
arr[i][j] =' ';
}
}
}
void Printf(char arr[Row][Line], int row, int line)
{
int cou = 0;
int i = 0;
for (i=0;i<row;i++)
{
int j = 0;
for (j = 0; j < line; j++)
{
printf(" %c ",arr[i][j]);
if(j<line-1)
printf("|");
}
printf("\n");
if (i < row - 1)
{
for (j = 0; j < line; j++)
{
printf("___");
if (j < line - 1)
printf("|");
}
}
printf("\n");
}
}
void player_move(char arr[Row][Line], int row, int line)//玩家下棋
{
int x = 0;
int y = 0;
while (1)
{
printf("玩家请输入坐标\n");
scanf_s("%d %d", &x, &y);
if (x <= row && y <= line && x >= 1 && y >= 1)
{
if (arr[x - 1][y - 1] == ' ')
{
printf("坐标合法,下棋成功\n");
arr[x - 1][y - 1] = '*';//玩家下棋标志为*
break;
}
else
printf("坐标被占用,请重新输入\n");
}
else
printf("坐标非法请重新输入\n");
}
}
void computer_move(char arr[Row][Line], int row, int line)
{
while (1)
{
int x = rand() % row;
int y = rand() %line;
if (arr[x ][y ] == ' ')
{
最后,在这个代码中我有一个点没有给读者讲清,比如随机数函数的使用,因为我的代码不好测试这个函数的使用规范,读者们可以看个猜数字游戏的博客看看这个函数是如何使用的,还有就是我为什么要把主函数放在源.c文件,而游戏具体函数实现放在game.c,这些函数声名则放在game.h中,好处如下一:随着我们技术的提升,我们要实现的功能只会越来越多,总不能都在主函数定义函数,那不就十几个人共用一台电脑(想想那场面,后面的程序员催促说,写完没啊,快点啊,到我写了,真是有点哭笑不得)。二:有利于我们隐藏我们的源码,这点就不展开说啦,好啦,一不小心就写了近六千多,我真不想写这么长,因为我一开始逛csdn的时候看到长文章就晕,我也不想别人看到我的文章也晕,我已经在尽量减少字数了,实在是代码有时候真是一环扣一环,不好省略,如果读者是比我还萌的萌新就看看我的思路,自己尝试写一写,卡壳再来看看完整代码,是大佬的话读者自便哈。