目录
🌱🌱 到目前为止,初阶C语言已经学了一半,相信大家学完了选择循环,函数,数组,一直想找些题目来练练手,今天就让我们来实现一下三子棋。也当做是复习之前学过的内容。
1、何为三子棋,三子棋的规则如何?
三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子走成一条线就视为胜利,而对方就算输了,但是三子棋在很多时候会出现和棋的局面。
2、 游戏的整体框架
该游戏我们采用分模块去实现:
- test.c ---- 测试游戏
- game.h ---- 函数的声明
- game.c ---- 游戏函数的实现
2.1 创建游戏菜单
test.c
首先肯定有main函数,做游戏时,main函数里面的东西尽量要少,因为是分模块写。
回到游戏设计本身,main函数里面包含一个test函数,这个test函数是通过你输入的值来进行菜单的选择,究竟是玩还是不玩,得看玩家自己。这里的选择采用switch。当然了,一个游戏的开始必然离开不了菜单,在此之前,还要打印menu函数。这里的开始整体逻辑与猜数字是一样的。猜数字我在之前的文章里写过,不熟悉的同学可以先去看看猜数字。下面放一下猜数字那篇博客的链接:http://t.csdn.cn/Z9JJD
#include <stdio.h> void menu() { printf("*********************\n"); printf("***** 1.play *****\n"); printf("***** 0.exit *****\n"); printf("*********************\n"); } void test() { int input = 0; do { menu(); printf("请输入:1/0->"); scanf("%d", &input); switch (input) { case 1: printf("sanziqi\n"); break; case 0: printf("退出游戏!\n"); break; default: printf("重新选择!\n"); } } while (input); } int main() { test(); return 0; }
接下来测试一下目前游戏代码是否能正常运行:
我们目前发现游戏代码没有错误,下面只需在switch(1)里面放置game(),来实现游戏的基本逻辑。
接下来我们来到game()函数里面,写三子棋,肯定要先有棋盘,没有棋盘,游戏根本无法进行。这个棋盘(3*3)不就是我们学过的数组嘛。
game.h
这里在创建的过程中,数组的行列需要从game.h里面导入,由于后面的game.c也需要从game.h中导入。所以为了避免代码的冗余,我们直接将#include <stdio.h>直接放到game.h里面。这里选用宏定义,其实也是对这个游戏的优化,我们可以将他改成10*10。
2.2 初始化棋盘
有了棋盘之后,第一步是清空棋盘,也就是对数组进行初始化。这里我们采用InitBoard()函数来表示初始化。 这里InitBoard( )里面的参数,我们传数组名,行,列。
game.c
而InitBoard( )真正实现的代码放在“game.c”这个文件里面,注意“game.c”要包括“game.h”这个头文件。
InitBoard(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++) { board[i][j] = ' '; } } }
2.3 打印棋盘
棋盘初始化后我们需要打印出棋盘来看看。不妨用DisplayBoard( )来表示棋盘的打印函数,
上图是我们最终要打印出来的棋盘。打印棋盘分为两个步骤,一个是打印数据,我们采用“ %c ”+“|”,但注意到最后一列是没有必要打印出“|”,所以这里用if语句判断一下。另一个是打印分割行,为了跟数据对齐,我们采用“---”+“|”,同理跟打印数据一样,最后一列无需打印“|”。注意,打印完一行要记得换行!接下来在game.c文件里面实现打印棋盘的代码:
DisplayBoard(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 < col - 1) { for (j = 0; j < col; j++) { printf("---"); if (j < col - 1) { printf("|"); } } } printf("\n"); } }
到了这里,可以先检测一下目前的代码是否能正常运行:
2.4 玩家下棋
有了棋盘并且还能将他打印出来,接下来就是游戏的核心之一——玩家下棋。
这里需要注意的是,玩家不是程序猿,他们不知道数组是从0下标开始的,所以在设计的过程中,需要对行列进行减一的操作。另外对于输入的值还要进行判断是否合法,比如说要考虑该坐标是否被占用,没被占用的话,不妨令玩家落子的符号规定为'*',被占用或者超出范围的话要提醒玩家。
PlayerMove(char board[ROW][COL],int row, int col) { printf("玩家走->:"); int x = 0; int y = 0; 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] = '*'; } else { printf("该位置已被占用!,请重新输入\n"); } } else { printf("超出范围,请重新输入\n"); } }
注意玩家下完棋之后要打印棋盘,否则会看不到你想要的结果,下面给出运行结果:
2.5 电脑下棋
该游戏的另一个核心就是电脑下棋,为了实现电脑下棋这个功能,我们需要创建随机数,
就需要在game.h里面包括以下两个头文件:1.<stdlib>, 2.<time.h>
此外我们还需要通过时间戳来随机下棋:
1.srand((unsigned int)time(NULL))—进行初始化(建议放在test(),这个只需运行一次即可)
2.x=rand%row, y=rand%col——生成随机数
我们用ComputerMove来表示电脑下棋。
和玩家下棋,电脑下棋需要判断是否被占用,如果没有被占用,就返回‘#’。
ComputerMove(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; while (1) { x = rand() % row; y = rand() % col; if (board[x][y] == ' ') { board[x][y] = '#'; break; } } }
2.6 判断输赢
由于该游戏规则的特殊性,每当玩家和电脑对弈一次后就需要判断是否已经产生输赢。来回下棋就需要while(1)来死循环,直到找到跳出循环条件,我们需要用Iswin()函数来判断,这个时候需要定义一些返回值来判断:
- 玩家赢——返回'*'
- 电脑赢——返回'#'
- 平局——返回'Q'
- 游戏继续——返回'C'
自然而然的,又需要定义一个char类型的变量来接受返回值
void game() { char board[ROW][COL];//创建棋盘 InitBoard(board,ROW,COL);//初始化棋盘 DisplayBoard(board, ROW, COL);//打印出棋盘 char ret = 0; while (1) { PlayerMove(board, ROW, COL);// 玩家下棋 DisplayBoard(board, ROW, COL);//打印出棋盘 ret = Iswin(board, ROW, COL); if (ret != 'C') { break; } ComputerMove(board, ROW, COL);//电脑下棋 DisplayBoard(board, ROW, COL);//打印出棋盘 ret = Iswin(board, ROW, COL); if (ret != 'C') { break; } } if (ret == '*') { printf("玩家赢!\n"); } else if (ret == '#') { printf("电脑赢!\n"); } else { printf("平局!\n"); } }
game.c
接下来game.c里面完成Iswin函数的实现:
1.判断行
2.判断列
3.判断对角线
4.判断棋盘是否已满(又需要引用Isfull函数来判断)
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[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ') return board[0][2]; //平局 if (Isfull(board, row, col) == 1) { return 'Q'; } return 'C'; }
接下来是Isfull函数的实现:
int Isfull(char board[ROW][COL], int row, int col) { int i = 0, j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { if (board[i][j] == ' ') { return 0;//棋盘未满 } } } return 1;//棋盘满了 }
3、整个游戏代码
- test.c
#include "game.h" void menu() { printf("*********************\n"); printf("***** 1.play *****\n"); printf("***** 0.exit *****\n"); printf("*********************\n"); } void game() { char board[ROW][COL];//创建棋盘 InitBoard(board,ROW,COL);//初始化棋盘 DisplayBoard(board, ROW, COL);//打印出棋盘 char ret = 0; while (1) { PlayerMove(board, ROW, COL);// 玩家下棋 DisplayBoard(board, ROW, COL);//打印出棋盘 ret = Iswin(board, ROW, COL); if (ret != 'C') { break; } ComputerMove(board, ROW, COL);//电脑下棋 DisplayBoard(board, ROW, COL);//打印出棋盘 ret = Iswin(board, ROW, COL); if (ret != 'C') { break; } } if (ret == '*') { printf("玩家赢!\n"); } else if (ret == '#') { printf("电脑赢!\n"); } else { printf("平局!\n"); } } void test() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); printf("请输入:1/0->"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏!\n"); break; default: printf("重新选择!\n"); } } while (input); } int main() { test(); return 0; }
- game.h
#pragma once #include <stdio.h> #include <stdlib.h> #include <time.h> #define ROW 3 #define COL 3 InitBoard(char board[ROW][COL], int row, int col); DisplayBoard(char board[ROW][COL], int row, int col); PlayerMove(char board[ROW][COL], int row, int col); ComputerMove(char board[ROW][COL], int row, int col); int Isfull(char board[ROW][COL], int row, int col);
- game.c
#include "game.h" InitBoard(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++) { board[i][j] = ' '; } } } DisplayBoard(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 < col - 1) { for (j = 0; j < col; j++) { printf("---"); if (j < col - 1) { printf("|"); } } } printf("\n"); } } PlayerMove(char board[ROW][COL],int row, int col) { printf("玩家走->:"); int x = 0; int y = 0; 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] = '*'; } else { printf("该位置已被占用,请重新输入\n"); } } else { printf("超出范围,请重新输入\n"); } } ComputerMove(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; while (1) { x = rand() % row; y = rand() % col; if (board[x][y] == ' ') { board[x][y] = '#'; break; } } } 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[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ') return board[0][2]; //平局 if (Isfull(board, row, col) == 1) { return 'Q'; } return 'C'; } int Isfull(char board[ROW][COL], int row, int col) { int i = 0, j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { if (board[i][j] == ' ') { return 0;//棋盘未满 } } } return 1;//棋盘满了 }
下面来试玩一下吧: