简介
三子棋,相信大家很多人小时候都玩过,横竖各画两线,在9个格子里面✔或者×,看谁先连成一线谁就胜利。在这里,我们的对手是电脑。
这个小游戏结合了数组,循环语句两大主要内容进行编写程序。再加上分文件编程,最后就能构成。
大体思路
分文件编程,好处就是程序简洁,分工有序,方便调试。这里,分成三个文件。分别是
game.h:头文件,把各个函数封装在一起,方便调用
game.c:游戏函数的定义
test.c:测试文件,用来实现最终效果
那么最主要的就是这个game.c文件了。思路主要为:初始化,打印棋盘,玩家下棋,电脑下棋,判断输赢(包含判断棋盘为满),这五个函数来写。
初始化以及打印
一般我们都是从主函数入手,首先要给出玩家一个菜单,明白要怎么操作,那么直接就打印出来即可。
void menu() { printf("**********************\n"); printf("********1.PLAY********\n"); printf("********0.EXIT********\n"); printf("**********************\n"); printf("**********************\n"); }
由于游戏简单,所以我们设定可以多次玩这个小游戏,将游戏决定权交给玩家,那么我们就用do-while语句进行封装再加上switch封装选择即可。
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);
进入游戏我们相先对棋盘进行初始化,初始化用空格字符即可。
void BoardInit(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] = ' '; } } }
接下来就是将棋盘打印出来;上面我们说过,只有画出线将它分成9个格子,这样子给玩家才有一种明朗的感觉。三子棋,很明显有三行三列,而分隔线只有2条横竖,所以我们还要进行-1,不必画出多余的线条。利用两个for循环就能打印出一个二维平面。
void BoardPrint(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"); } } }
效果:
玩家和电脑下棋
接下来就是开始下棋了。先从玩家下棋开始,在玩家眼中,下标0对应的是坐标1,所以在输入坐标时,需要对玩家输入值进行-1的操作。在棋盘中,我们要判断玩家输入坐标是否超过范围,不超过范围时,该坐标是否被占领。这些问题想清楚,我们就可以编写代码了。
void PlayerMove(char board[ROW][COL], int row, int col) { int x = 0, y = 0; printf("玩家下棋:\n"); while (1) { printf("请输入下棋位置坐标,用空格隔开"); 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"); } } }
电脑下棋,我们要知道怎么让电脑自动下棋,在这里,我们利用随机数来进行随机下棋,只需将随机数进行取模即可。再判断是否为空的。
//随机数种子
srand((unsigned int)time(NULL));
void ComputerMove(char board[ROW][COL], int row, int col) { int x=0, y=0; printf("电脑下棋:\n"); while (1) { //rand()代表随机数 x = rand() % row; y = rand() % col; if (board[x][y] == ' ') { board[x][y] = '#'; break; } } }
判断是否连成一条线
我们都知道,连成一条线一般分为三种,横,竖,斜。我们只需要统计行或列以及对角线的相同字符个数即可。
char IsWin(char board[ROW][COL], int row, int col) { //行项 for (int i = 0; i < row; i++) { //Ni记录人,电脑,空 char NI = board[i][0]; //num记录相同的 int num = 0; if (NI != ' ') { num = 1; } for (int j = 1; j < col; j++) { if (NI != ' '&&board[i][j]==NI) { num++; } if (num == row) { return NI; } } } //列项 for (int i = 0; i < col; i++) { //Ni记录人,机,空 char NI = board[0][i]; //num记录相同的 int num = 0; if (NI != ' ') { num = 1; } for (int j = 1; j < row; j++) { if (NI != ' ' && board[j][i] == NI) { num++; } if (num == row) { return NI; } } } //正对角线 char NI1 = board[0][0]; int num1 = 0; if (NI1 != ' ') { num1 = 1; } for (int i = 1; i < row; i++) { if (NI1 != ' ' && board[i][i] == NI1) { num1++; } if (num1 == row) { return NI1; } } //反对角线 char NI2 = board[0][row - 1]; int num2 = 0; if (NI2 != ' ') { num2 = 1; } for (int i = 1,j=1; i < row,j<col; i++,j++) { if (NI2 != ' ' && board[i][row - 1 - j]==NI2) { num2++; } if (num2 == row) { return NI2; } } //平局 if (IsFull(board, row, col) == 1) { return 'Q'; } //继续 return 'C'; }
在这里,我们根据返回值的形式判断输赢,
当棋盘满时,即为平局。
最后的封装
进入游戏后,我们进入的是game()函数,我们先定义一个二维数组,对它进行初始化,打印,接着就是玩家和电脑下棋,下棋时,我们用循环进行多次下棋,并根据上面的返回值进行判断即可。
在这里,我们用了宏定义定义了行数和列数,
这样做,我们只需修改这两个宏变量的值,就能改变棋盘的大小。
原代码
//test.c #include"game.h" void menu() { printf("**********************\n"); printf("********1.PLAY********\n"); printf("********0.EXIT********\n"); printf("**********************\n"); printf("**********************\n"); } void game() { char board[ROW][COL] = { 0 }; //初始化 BoardInit(board, ROW, COL); //打印 BoardPrint(board, ROW, COL); //开始下棋 char ret = 0; while (1) { //人 PlayerMove(board, ROW, COL); BoardPrint(board, ROW, COL); //进行判断输赢 ret = IsWin(board, ROW, COL); //非继续就退出循环 if (ret != 'C') { break; } //电脑 ComputerMove(board, ROW, COL); BoardPrint(board, ROW, COL); //判断 ret = IsWin(board, ROW, COL); if (ret != 'C') { break; } } if (ret == '*') { printf("你赢了。\n"); } else if (ret == '#') { printf("电脑赢了。\n"); } else { printf("平局。\n"); } } int main() { //随机种子 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); return 0; }
//game.h #pragma once//防止头文件重复包含 #include<stdio.h> #include<stdlib.h> #include<time.h> //行数和列数,可改变 #define ROW 3 #define COL 3 //初始化 void BoardInit(char board[ROW][COL], int row, int col); //打印棋盘 void BoardPrint(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" //初始化 void BoardInit(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 BoardPrint(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 PlayerMove(char board[ROW][COL], int row, int col) { int x = 0, y = 0; printf("玩家下棋:\n"); while (1) { printf("请输入下棋位置坐标,用空格隔开"); 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 ComputerMove(char board[ROW][COL], int row, int col) { int x=0, y=0; printf("电脑下棋:\n"); while (1) { x = rand() % row; y = rand() % col; if (board[x][y] == ' ') { board[x][y] = '#'; break; } } } //判断棋盘是否已经满了 int IsFull(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 IsWin(char board[ROW][COL], int row, int col) { //行项 for (int i = 0; i < row; i++) { //Ni记录人,机,空 char NI = board[i][0]; //num记录相同的 int num = 0; if (NI != ' ') { num = 1; } for (int j = 1; j < col; j++) { if (NI != ' '&&board[i][j]==NI) { num++; } if (num == row) { return NI; } } } //列项 for (int i = 0; i < col; i++) { //Ni记录人,机,空 char NI = board[0][i]; //num记录相同的 int num = 0; if (NI != ' ') { num = 1; } for (int j = 1; j < row; j++) { if (NI != ' ' && board[j][i] == NI) { num++; } if (num == row) { return NI; } } } //正对角线 char NI1 = board[0][0]; int num1 = 0; if (NI1 != ' ') { num1 = 1; } for (int i = 1; i < row; i++) { if (NI1 != ' ' && board[i][i] == NI1) { num1++; } if (num1 == row) { return NI1; } } //反对角线 char NI2 = board[0][row - 1]; int num2 = 0; if (NI2 != ' ') { num2 = 1; } for (int i = 1,j=1; i < row,j<col; i++,j++) { if (NI2 != ' ' && board[i][row - 1 - j]==NI2) { num2++; } if (num2 == row) { return NI2; } } //平局 if (IsFull(board, row, col) == 1) { return 'Q'; } //继续 return 'C'; }