前言
三子棋,童年的回忆,只要三个棋子练成一条线,不论是水平、垂直、还是倾斜的直线,即为胜利。刚好最近在学C语言,就试着写个简易版三子棋,大致如下图:
一、思路
首先创建三个项目,一个test.c文件、一个game.c文件、一个game.h文件;test.c文件主要是用来实现游戏的逻辑,game.c文件用来实现每个功能模块,game.h文件用来存放头文件和宏定义。
二、代码实现
1.test.c文件
首先写一个主函数,里面有一个test函数用于测试逻辑。
int main()
{
test();
return 0;
}
然后是test函数的内容:
void test()
{
srand((unsigned int)time(NULL));
//用于电脑产生随机值时,用来初始化rand()的
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d",&input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
}
进入test函数主要是do…while()循环,为了实现多次玩游戏;然后进入menu()函数进行简单的打印界面,如下:
void menu()
{
printf("*********************\n");
printf("******* 1.play ******\n");
printf("******* 0.exit ******\n");
printf("*********************\n");
}
do…while()循环中嵌套一个switch()函数,进行玩游戏和退出的选择,主要功能逻辑在game()函数中实现,代码如下:
#include"game.h"
void game()
{
char Board[ROW][COL];
//ROW和COL在game.h中定义的宏,方便后续更改
char ret = 0;
Init_Board(Board, ROW, COL);
Print_Board(Board, ROW, COL);
while (1)
{
Player_move(Board, ROW, COL);
Print_Board(Board, ROW, COL);
ret = is_win(Board, ROW, COL);
if (ret != 'C')
{
break;
}
Computer_move(Board, ROW, COL);
Print_Board(Board, ROW, COL);
ret = is_win(Board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == '*')
{
printf("玩家赢\n");
}
else if (ret == '#')
{
printf("电脑赢\n");
}
else if (ret == 'Q')
{
printf("平局\n");
}
}
game函数中首先是对棋盘进行初始化,用Init_Board(Board, ROW, COL)函数进行初始化,传过去数组名和二维数组的行和列;然后利用Print_Board()函数打印棋盘,棋盘有了之后,下面就是开始下棋;Player_move()函数实现人下棋,下完之后利用Print_Board()函数打印;然后电脑通过Computer_move()函数实现下棋,然后打印;在while循环中进行多次下棋,要不就一次就结束了。最后定义一个字符变量ret,用来接收is_win()函数的返回值,根据不同的字符来判断谁赢,平局和继续下棋。
2.game.h文件
game.h文件主要用来存放库函数的头文件,宏定义,和test.c中函数的声明,以便game.h中方便使用;且可以通过引用game.h文件来使用其中的库函数的头文件,代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//srand和rand所需的头文件
#define ROW 3
#define COL 3
//初始化棋盘
void Init_Board(char Board[ROW][COL],int row,int col);
//打印棋盘
void Print_Board(char Board[ROW][COL], int row, int col);
//玩家走
void Player_move(char Board[ROW][COL], int row, int col);
//电脑走
void Computer_move(char Board[ROW][COL], int row, int col);
//判断输赢函数
char is_win(char Board[ROW][COL], int row, int col);
例如此中的#include<stdio.h>头文件,test.c文件只需要#include"game.h"这样,就可以使用#include<stdio.h>和其他game.h的头文件;函数的声明,对于数组,需要写出数组元素的类型和素组的行和列
3.game.c文件
game.c文件主要进行函数的实现,也是最核心的部分
3.1棋盘初始化
代码如下,主要是对二位数组的遍历,然后将其都赋值空
void Init_Board(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] = ' ';
}
}
}
3.2棋盘打印
棋盘的打印,简易棋盘由—和|组成的,为了视觉效果,第一行打印空格字符和|,第二行打印___和|组成的,就这样组成简易棋盘
第一行:
1,2,3中放字符,剩下的里面放空格
第二行:
同样的,1,2,3位置放字符,且第二行的1,2,3和第一行的1,2,3是一样的,剩下的放空格,然后,两行组成新的一行,就如下所示:
将新的一行循环三次打印如下:
但是此时,却多了最后一行,需要限制条件,最后一行不打印,即和我们的棋盘一致了
代码如下:
void Print_Board(char Board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf(" %c ", Board[i][j]);
if (j < col-1)
printf("|");
}
printf("\n");
//上面打印第一行
if (i <row - 1)
{
for (int j = 0; j < col; j++)
{
printf("---");
if (j < col-1)//最后一行限制条件
printf("|");
}
printf("\n");
//上面打印第二行
}
}
}
3.3玩家下棋
玩家下棋首先需要判断坐标合法化,因为棋盘是一个3x3的棋盘,横纵的坐标都只能是0-2的整数,然后再判断一下坐标是否为空格,是空格才能下,且还有一步小转化,非程序员会认为第一个位置是(1,1),但是数组里面却是(0,0);代码如下:
void Player_move(char Board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家请下棋:>");
while (1)
{
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (Board[x - 1][y - 1] == ' ')
{
Board[x - 1][y - 1] = '*';
break;
}
else
{
printf("坐标被占用,请重新输入\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
3.4电脑下棋
电脑下棋是利用rand()函数产生的随机值进行对数组行和列取余得到的随机合法坐标,其中用到rand和srand函数,因为rand不能单独使用,需要srand进行初始化,srand利用时间戳进行初始化;代码如下:
void Computer_move(char Board[ROW][COL], int row, int col)
{
printf("电脑走:>\n");
while (1)
{
int x = rand()% row;
int y = rand()% col;
if (Board[x][y] == ' ')
{
Board[x][y] ='#';
break;
}
}
}
3.5判断输赢
下棋的过程中,有四种情况:玩家赢、电脑赢、平局、继续下;因此定义一个字符变量ret,当其为*玩家赢,#电脑赢,Q平局,C继续下
赢:有四种:横着三子一条线,竖着三子一条线,主对角线三子一条线,副对角线三子一条线;这四种情况都是赢
平局:即棋盘被下满,但都没有赢
继续下:棋盘没满,也没有人赢
代码如下:
int is_full(char Board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (Board[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
char is_win(char Board[ROW][COL], int row, int col)
{
for (int 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 (int j = 0; j < col; j++)
{
if (Board[0][j] == Board[1][j] && Board[1][j] == Board[2][j] && Board[0][j] != ' ')
{
return Board[0][j];
}
}
for (int i = 0; i < row; i++)
{
if (Board[0][0] == Board[1][1] && Board[1][1] == Board[2][2] && Board[1][1] !=' ')
{
return Board[1][1];
}
}
for (int i = 0; i < row; i++)
{
if (Board[2][0] == Board[1][1] && Board[1][1] == Board[0][2] && Board[1][1] != ' ')
{
return Board[1][1];
}
}
if (is_full(Board, row, col) == 1)
{
return 'Q';
}
return 'C';
}
is_full()函数是用来遍历数组,来判断都没赢的条件下,数组是否已满;此判断输赢的函数比较基础,待日后完善。
三、完整代码
//test.c全部代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void game()
{
char Board[ROW][COL];
char ret = 0;
Init_Board(Board, ROW, COL);
Print_Board(Board, ROW, COL);
while (1)
{
Player_move(Board, ROW, COL);
Print_Board(Board, ROW, COL);
ret = is_win(Board, ROW, COL);
if (ret != 'C')
{
break;
}
Computer_move(Board, ROW, COL);
Print_Board(Board, ROW, COL);
ret = is_win(Board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == '*')
{
printf("玩家赢\n");
}
else if (ret == '#')
{
printf("电脑赢\n");
}
else if (ret == 'Q')
{
printf("平局\n");
}
}
void menu()
{
printf("*********************\n");
printf("******* 1.play ******\n");
printf("******* 0.exit ******\n");
printf("*********************\n");
}
void test()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d",&input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
//game.h全部代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3
//初始化棋盘
void Init_Board(char Board[ROW][COL],int row,int col);
//打印棋盘
void Print_Board(char Board[ROW][COL], int row, int col);
//玩家走
void Player_move(char Board[ROW][COL], int row, int col);
//电脑走
void Computer_move(char Board[ROW][COL], int row, int col);
//判断输赢函数
char is_win(char Board[ROW][COL], int row, int col);
//game.c全部代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void Init_Board(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] = ' ';
}
}
}
void Print_Board(char Board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf(" %c ", Board[i][j]);
if (j < col-1)
printf("|");
}
printf("\n");
if (i <row - 1)
{
for (int j = 0; j < col; j++)
{
printf("---");
if (j < col-1)
printf("|");
}
printf("\n");
}
}
}
void Player_move(char Board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家请下棋:>");
while (1)
{
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (Board[x - 1][y - 1] == ' ')
{
Board[x - 1][y - 1] = '*';
break;
}
else
{
printf("坐标被占用,请重新输入\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
void Computer_move(char Board[ROW][COL], int row, int col)
{
printf("电脑走:>\n");
while (1)
{
int x = rand()% row;
int y = rand()% col;
if (Board[x][y] == ' ')
{
Board[x][y] ='#';
break;
}
}
}
int is_full(char Board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (Board[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
char is_win(char Board[ROW][COL], int row, int col)
{
for (int 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 (int j = 0; j < col; j++)
{
if (Board[0][j] == Board[1][j] && Board[1][j] == Board[2][j] && Board[0][j] != ' ')
{
return Board[0][j];
}
}
for (int i = 0; i < row; i++)
{
if (Board[0][0] == Board[1][1] && Board[1][1] == Board[2][2] && Board[1][1] !=' ')
{
return Board[1][1];
}
}
for (int i = 0; i < row; i++)
{
if (Board[2][0] == Board[1][1] && Board[1][1] == Board[0][2] && Board[1][1] != ' ')
{
return Board[1][1];
}
}
if (is_full(Board, row, col) == 1)
{
return 'Q';
}
return 'C';
}
总结
小游戏,大细节,受益良多,多动手,终有所获。