游戏整体思路:
1.编写前的准备
2.游戏的规则
3.游戏的菜单
4.井字棋棋盘的打印
5.玩家下棋与计算机下棋的实现
6.游戏的输赢判断
编写前的准备:
编写前首先需要准备三个文件:
1.负责测试游戏逻辑的 .c 文件。 - test.c
2.负责头文件包含以及函数声明的 .h 文件。 - game.h
3.负责实现游戏函数的 .c 文件。 - game.c
游戏的规则
双方依次在九宫格棋盘上摆放棋子,率先将自己的三个棋子走成一条线就视为胜利。
由此可知,在开始游戏后首先会打印棋盘,其次玩家以输入坐标的方式进行下棋;下棋后电脑在一个空余的棋盘上进行下棋,当双方其中一方下完棋后为一条直线后,游戏判断为赢,否则为平局,游戏结束后重新选择选项是否继续游戏,否则退出。
游戏菜单的打印
1.无论在进入什么游戏,首先应该有一个菜单,负责让玩家做选项,选择游戏时则开始游戏,选择退出时则退出游戏,选择错误则重新作选择,要实现该想法我们应该使用一个 do{ } while( ); 循环语句;
void menu() { printf("******************************\n"); printf("********** 1.play *********\n"); printf("******************************\n"); printf("********** 0.exit *********\n"); printf("******************************\n"); } int main() { int chose = 0; do { menu(); printf("Please enter your chose:>"); scanf("%d",&chose); switch (chose) { case 1: printf("Tic-Tac-Toe\n"); break; case 0: printf("Quit the game\n"); break; default: printf("Input erro\n"); } } while (chose); return 0; }
在 test.c(负责测试游戏逻辑的.c文件)中创建一个 menu();函数,用来实现游戏菜单的显现,并在main()函数中的do{ } while()语句中调用,每当打开游戏或者是结束一局游戏后,都会调用该函数,打印一遍菜单;
将游戏菜单的打印实现后,应该实现的就是开始游戏,退出游戏的选择;
从菜单可知,键盘输入1为开始游戏,0为退出游戏,既然是键盘输入,我们选择的则为getchar( ) \ scanf( ); 在使用getchar()函数时,可能会使下一次输入时读取到 ‘ \n ’ ,所以此处选择用scanf();,并将scanf()读取到的数据值赋给 int类型变量chose内 ;并用 开关语句(多分支语句)swithc( ) case: 语句,并将变量chose作为switch()语句的判断条件进行数据的判断:若键盘输入1,则开始三子棋游戏;若键盘输入0,则提示退出游戏并退出;若键盘输入其他数,则提示输入错误,并提示重新输入选项。
同时将变量chose作为该处do{ } while( );语句的循环条件,若输入1,则恒为真,并进入游戏;输入0,则为假,跳出循环;输入其他时也恒为真,并提示重新输入。
游戏棋盘的打印
三子棋游戏中,玩家和电脑分别要在棋盘内进行下棋;所以在下棋之前,首先要实现游戏棋盘的打印和显现;三子棋的游戏是在一个九宫格内进行的,玩家和电脑需要在不同的坐标进行下棋,“九宫格” 相当于是3*3的小方格,从这里我们可以联想到 横为3列为3 的二维数组。
在switch语句中的 case 1 语句内创建一个game函数,每当玩家选择 1 时,则默认为游戏开始,进入game()函数;
void game() { //创建一个二维数组 char board[ROW][COL] = {0}; //棋盘的初始化 Intboard(board, ROW, COL); //棋盘的打印 Display(board, ROW, COL); }
在打印出棋盘前,应有相应的一个二维数组,为了加强代码的健壮性,可在负责头文件包含以及函数声明的 .h 文件 game.h 文件中创建一个 #define 定义的标识符常量
#define ROL 3 //行
#define COL 3 //列
确保当文件需要修改行列时可以一针见血。(应在 test.c 文件中#include " "来包含此文件)
三子棋棋盘的构造由空格、电脑与玩家两种不同的棋子以及每个格子的分界线组成,为了下步棋盘的美观及整齐性,应先将数组初始化为空格;在 game( ) 函数内调用一个Intboard()函数,用来初始化所创建的数组(在头文件中应先声明该函数,并在 game.c 文件中实现该函数),初始化空格有两种方法:
1.使用 memset( ) 函数初始化数组的内存;
#include<string.h>//在使用memset()函数前应包含string.h头文件 void Intboard(char board[ROW][COL],int row ,int col) { memset(&board[0][0], ' ',row*col*sizeof(board[0][0])); }
memset 函数为初始化函数,可以把一段连续的内存初始化某个值。
2.使用迭代的方式将数组的各个元素遍历一遍,并将每个元素都初始化为 ' '。
void Intboard(char board[ROW][COL], int row, int col) { for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { board[i][j] = ' '; } } }
此时二维数组内的元素都被初始化为了 ‘ ’ ,但并未出现棋盘该有的样子,再将棋盘的边框以打印的方式显现出来时,才能得到一个较为美观的棋盘。
在 game( ) 函数内调用的 Intboard( ) 函数后再调用一个Display( )函数,(同样应先在 game.h 文件中先声明 ,并在game.c 文件中实现该函数)。
void Display(char board[ROW][COL], int row, int col) { int i = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { printf(" %c ", board[i][j]); //将每个' %c '看为一组 if (j < col - 1) { printf("|");//每打印完一次' %c '打印一次 '|' ,最后一次则不打印 } } printf("\n");//每打印完一行后打印换行 if (i < row - 1) {//每打印一行后打印以下内容,最后一次则不打印 int j = 0; for (j = 0; j < col;j++) { printf("---");//与上对应,将'---'看为一组进行打印 if (j < col - 1) { printf("|");//每打印完一组'---',打印一个'|'作为分割线,最后一次不打印 } } } printf("\n");//打印完一组后的换行 } }
玩家下棋以及计算机下棋的实现
玩家下棋
玩家与计算机的下棋机制为回合制,在 game() 函数内玩家与计算机下棋应使用while循环,每下完一次棋判断一次输赢。
char ret = 0; while (1) { //玩家下棋; Playermove(board,ROW,COL); //玩家下棋后打印棋盘 Display(board, ROW, COL); }
void Playermove(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; while (1) { printf("The player continue:>\n"); printf("Please enter your coordinate:>"); scanf("%d%d", &x, &y); //判断输入坐标的合法性 if (x > 0 && x <= row && y>0 && y <= col) { if (board[x - 1][y - 1] == ' ') { board[x - 1][y - 1] = '*'; break; } else { printf("Coordinates are occupied.\n"); } } else { printf("The coordinate is erro;\n"); } } }
在while()中调用一个Playermove()函数(并在 game.c 文件中实现该函数),当轮到玩家下棋时,打印提示“玩家下棋”以及“请输入坐标",定义两个变量x 与 y,用scanf()从键盘中获取两个数并赋值给x,y的变量。
当x,y获取到两个数据时,应先判断坐标的合法性。
确保输入的坐标范围为 1-3 ,若是输入坐标不合法,则提示“坐标不合法,请重新输入坐标”。
当坐标合法时则需要判断坐标是否被占用,若输入的坐标被占用时则提示“坐标被占用”。
如果输入的标既合法且未被占用(字符内为空格),则将 ' * ' 赋值给该坐标;(输入的坐标范围为1-3,数组的下标为0-2,故下棋时的坐标行列都得-1)。
玩家下完棋后应再打印一次棋盘。
计算机下棋
在while()中调用一个Coputermove()函数(并在 game.c 文件中实现该函数),当轮到电脑下棋时,打印提示“计算机下棋”。计算机下棋时应使用随机数,在此之前应创建一个随机数生成器。
int main() { srand((unsigned int)time(NULL)); }
在main()函数中使用 srand( ) 函数以时间戳来创建一个随机数生成器。(应包含stdlib.h文件以及time.h文件),并在Computermove()函数中调用rand()函数来生成随机数(计算机生成的数可以以下标来设定,故%3,取出的范围则为0-2,确保坐标的合法性)
#include<time.h> #include<stdlib.h> void Computermove(char board[ROW][COL], int row, int col) { printf("Computer move:>\n"); int x = 0; int y = 0; while (1) { x = rand() % 3; y = rand() % 3; if (board[x][y] == ' ') { board[x][y] = '#'; break; } } }
当该坐标为 ’ ‘ 时,则代表该处坐标未被占用,计算机则以此坐标落子。当计算机落完子后,应在 game( ) 函数内再打印一次棋盘,并判断输赢。
游戏输赢的判断
游戏每进行一次则需要判断输赢一次
void game() { //创建一个二维数组 char board[ROW][COL] = {0}; //棋盘的初始化 Intboard(board, ROW, COL); //棋盘的打印 Display(board, ROW, COL); //玩家下棋以及计算机下棋的实现 char ret = 0; while (1) { //玩家下棋; Playermove(board,ROW,COL); //玩家下棋后打印棋盘 Display(board, ROW, COL); //判断输赢 ret = Iswin(board, ROW, COL); if (ret != 'C') { break; } //计算机下棋 Computermove(board,ROW,COL); Display(board, ROW, COL); ret = Iswin(board, ROW, COL); if (ret != 'C') { break; } } if (ret == '*') { printf("Players win!\n"); } else if (ret == '#') { printf("Computers win!\n"); } else printf("The game draw.\n"); }
创建一个char类型的变量ret用来接收 Iswin( ) 函数用来判断输赢的返回值。在此处调用一个名为 Iswin( ) 的char类型的函数。
当电脑赢时,则返回电脑的棋子 - #
当玩家赢时,则返回玩家的棋子 - *
当游戏平局时,返回 - 'Q'
当游戏都不满足上述条件时,则返回 - ’C‘
char Iswin(char board[ROW][COL], int row, int col) { int i = 0; for (i = 0; i < row; i++) { if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ') { return board[i][0]; } } for (i = 0; i < col; i++) { if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ') { return board[0][i]; } } if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ') { return board[0][0]; } if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[2][0] != ' ') { return board[2][0]; } if (Isfull(board, row, col)) { return 'Q'; } return 'C'; }
赢的方式为三子连成一线,共有三行、三列、两对点八种结果。
除了判断是否赢还需判断游戏是否平局。
在该函数内再调用一个int类型的自定义函数 Isfull( ) ;
int Isfull(char board[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { if (board[i][j] == ' ') { return 0; } } } return 1; }
当未出现任何赢家时则遍历一遍数组,若是数组中其中一个元素为空格,则返回0,if语句条件不成立,返回 ’C‘ ,若是遍历一遍数组内都没有元素为空格,则说明棋盘已满,且未出现赢家,则返回 ’Q‘;
回到 game( ) 函数
若是返回不为 ' C ' ,则跳出循环
若是返回 ' * ',则玩家赢
若是返回 ' # ',则电脑赢
若是返回 ' Q ',则平局
代码分享
test.c
#include<stdio.h> #include<string.h> #include"game.h" void game() { //创建一个二维数组 char board[ROW][COL] = {0}; //棋盘的初始化 Intboard(board, ROW, COL); //棋盘的打印 Display(board, ROW, COL); //玩家下棋以及计算机下棋的实现 char ret = 0; while (1) { //玩家下棋; Playermove(board,ROW,COL); //玩家下棋后打印棋盘 Display(board, ROW, COL); //判断输赢 ret = Iswin(board, ROW, COL); if (ret != 'C') { break; } //计算机下棋 Computermove(board,ROW,COL); Display(board, ROW, COL); ret = Iswin(board, ROW, COL); if (ret != 'C') { break; } } if (ret == '*') { printf("Players win!\n"); } else if (ret == '#') { printf("Computers win!\n"); } else printf("The game draw.\n"); } void menu() { printf("******************************\n"); printf("********** 1.play *********\n"); printf("******************************\n"); printf("********** 0.exit *********\n"); printf("******************************\n"); } int main() { srand((unsigned int)time(NULL)); int chose = 0; do { menu(); printf("Please enter your chose:>"); scanf("%d",&chose); switch (chose) { case 1: game(); break; case 0: printf("Quit the game\n"); break; default: printf("Input erro\n"); } } while (chose); return 0; }
game.h
#define ROW 3 #define COL 3 //初始化棋盘 void Intboard(char board[ROW][COL], int row, int col); //棋盘的打印,显示 void Display(char board[ROW][COL], int row, int col); //玩家下棋 void Playermove(char board[ROW][COL], int row, int col); //电脑下棋 void Computermove(char board[ROW][COL], int row, int col); //判断输赢 char Iswin(char board[ROW][COL], int row, int col);
game.c
#include"game.h" #include<stdio.h> int Isfull(char board[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { if (board[i][j] == ' ') { return 0; } } } return 1; } void Intboard(char board[ROW][COL], int row, int col) { //memset(board, ' ', 9);//使用memset()初始化内存 for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { board[i][j] = ' '; } } } void Display(char board[ROW][COL], int row, int col) { int i = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { printf(" %c ", board[i][j]); if (j < col - 1) { printf("|"); } } printf("\n"); if (i < row - 1) { int j = 0; for (j = 0; j < col;j++) { printf("---"); if (j < col - 1) { printf("|"); } } } printf("\n"); } } void Playermove(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; while (1) { printf("The player continue:>\n"); printf("Please enter your coordinate:>"); scanf("%d%d", &x, &y); //判断输入坐标的合法性 if (x > 0 && x <= row && y>0 && y <= col) { if (board[x - 1][y - 1] == ' ') { board[x - 1][y - 1] = '*'; break; } else { printf("Coordinates are occupied.\n"); } } else { printf("The coordinate is erro;\n"); } } } #include<time.h> #include<stdlib.h> void Computermove(char board[ROW][COL], int row, int col) { printf("Computer move:>\n"); int x = 0; int y = 0; while (1) { x = rand() % 3; y = rand() % 3; if (board[x][y] == ' ') { board[x][y] = '#'; break; } } } char Iswin(char board[ROW][COL], int row, int col) { int i = 0; for (i = 0; i < row; i++) { if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ') { return board[i][0]; } } for (i = 0; i < col; i++) { if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ') { return board[0][i]; } } if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ') { return board[0][0]; } if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[2][0] != ' ') { return board[2][0]; } if (Isfull(board, row, col)) { return 'Q'; } return 'C'; }
如有不足之处请兄弟萌批评指正!!!
敲码不易 记得三连!!!